Notes

姚善
2023-12-01
//updating
WV:重写WebViewClient的WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)拦截相应的请求,从本地读取相应资源

缓存控制。延迟加载js: onPageFinished()的回调意味着页面加载完,在JS执行完后才会触发,倘若我们要加载的页面使用了JQuery,会在处理完DOM对象执行完$(document).ready(function() {})才渲染并显示页面,所以需(前端)延迟js加载。控制台或prompt回调或通过注解方法实现java与js的交互,同时移除系统开放的JS接口( 针对3.0以上if (Build.VERSION.SDK_IN  T    >= Build.VERSION_CODES.HON  EYCOMB) {            dealJavascriptLeak();        }),禁止注入Java Object类的方法(getClass,hashCode,notify,notifyAll,equals,toString)。 严格控制js注入入口。


enum:在编译时转换成对应的final类:public enum Season{    SPRING,    SUMMER }
Compiled:public final  class com.c.Season extends  java.lang.Enum(实现了Serializable,Comparable,默认的只有同类型的enum才进行比较,不同类型的enum的比较可复写compareTo();)<com.c.Season> {  public  static final    com.c.SeasonSPRING;  public static final com.c.Season SUMMER;    public static com.c.Season[] values();//  public static com.c.SeasonvalueOf(java.lang.String);  static{//实例化SPRING, SUMMER并构建数组}; }                                                                                                                                                                                                                                                                                                                    
ThreadLocal(如作用在Looper):线程成员量(  把非线程共享数据存在了线程的ThreadLocalMap的一成员变量里  ) ,TL.set()或重写initialValue()初始化thread.threadLocals后才能被使用(使用场景一线程一实例,可考虑私有静态化,即一直存在ThreadLocal的强引用,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove之;要调用ThreadLocalMap的getEntry或set或remove(删除不再需要的ThreadLocal))(因为默认的Object value为空),Object TL.get)():在TL.getMap(Thread.currentThread()),获得了一ThreadLocalMap(TL里静态类)对象:即Thread里的ThreadLocalMap<ThreadLocal, Object> threadLocals变量 ,传入TL::this 获取Object。 以弱引用的ThreadLocal为键,Thread可能不止对应一ThreadLocal。


new Handler(new HandlerThread("如Service优先级的IntentService里的")‘.start()’..getLooper()).postAtFrontOfQueue/postDelayed/post(new Runnable(run方法体的开始部分android.os.Process.setThreadPriority(priority);一般的工作者线THREAD_PRIORITY_BACKGROUND;对于优先级很低的线THREAD_PRIORITY_LOWEST));            handler.sendMessage()->MessageQueue.enqueueMessage(msg, uptimeMillis)到MessageQueue队列;Looper.prepare()在每个线程只允许执行一次,Looper.loop()不断提取出达到触发条件的Message,Message.dispatchMessage()后,交给Handler.handleMessage(),再把分发后的Message回收到消息池,以便重复利用。无消息时退出循环  (Handler中有Looper和MessageQueue;     Looper有MessageQueue;MessageQueue有一组待处理的Message;Message有Handler; static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();)

AsyncTask抽象泛型:abstract class AsynTask<Params, Progress, Result>,SerialExcutor(ArrayDeque<Runnable>)任务队列化, THREAD_POOL_EXCUTOR任务执行, 主线程的Handler->主线程的AsyncTask.


提升进程优先级(1.前台进程( FOREGROUND_APP)   2.可视进程(VISIBLE_APP )   3. 次要服务进程(SECONDARY_SERVER )   4.后台进程 (HIDDEN_APP)   5.内容供应节点(CONTENT_PROVIDER)   6.空进程(EMPTY_APP)):监控屏锁广播,在屏幕锁屏时启动用户无感知的 A,解锁时再销毁;前台S: 于 18- :startForeground(ID, new Notification()),发送空的Notification ,图标则不显;其它在需要提优先级的service 里启动一个InnerService,两个服务同时startForeground,且绑定同样的 ID(的 Notification),Stop 掉Inner,,,通知栏图标即被移除;  在AndroidManifest.xml中intent-filter里android:priority = "1000"越小则优先级越低,也适用于广播;
拉活:注册第三方应用广播或系统广播;将 S 设为START_STICKY(onStartCommand里return Service.START_STICKY)系统会在 Service 挂掉后(会保留start状态,但不保留传入的intent)适当拉活之,在开始状态,在创建service后将保证调用onstartCommand;START_NOT_STICKY 在运行onStartCommand后service进程被kill后,并且没有新的intent传递给它,Service将移出开始状态,并且直到新的明显的方法(startService)调用才重新创建。START_REDELIVER_INTENT 在运行onStartCommand后service进程被kill后,系统将会再次启动service,并传入最后一个intent给onstartCommand。直到调用stopSelf(int)才停止传递intent。如果在被kill后还有未处理好的intent,那被kill后服务还是会自动启动,所以onstartCommand不会接收到任何null的intent. service里ondestory重启。Application加Persistent属性。


Bitmap内存(与屏幕像素密度无关)getRowBytes()*getHeight()(px) = 高 *宽 * 1像素占用的字节数( 由BitmapFactory.Options.inPreferredConfig  Bitmap.Config(图片格式:ARGB_8888(4 bytes)>ARGB_4444=RGB_565>ALPHA_8)枚举量决定) :矩阵/Options  options = new BitmapFactory.Options();options.inSampleSize =2 ;options.inMutable = true;bitmap = BitmapFactory.decodeFile(path,options);iv.setImageBitmap(bitmap);   options.inBitmap = bitmap;
BitmapRegionDecoder.newInstance().decodeRegion(newRect, options)


启动:Static块(如ContentProvider中在Static Block中初始化一些UriMatcher),IO阻塞、高CPU占用(渲染、解析,编解码),MultiDex:应用首次冷启动时解压dex文件以及优化dex会产生卡顿,虽然在Application。attachBaseContext(Context)UI线程的阻塞没引发ANR(adb pull /data/anr/traces.txt C:\: ANR问题 , “ANR”关键词;如果是ForceClosed 和其它异常退出信息,"Fatal" 关键词;),可启动App的时候启动新线程执行MultiDex,install(Context),要确保启动以及启动MultiDex。install(Context)所需的类都在主dex里面(手动分包),需处理线程同步,懒加载, 界面预加载,Application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {startstop;前台。onActivityCreated(Bundle可用于区别是否是被系统所回收的Activity)初始化UI相关,通过unregisterActivityLifecycleCallbacks来避免重复的初始化});


类文件从被加载 - 卸载:加载(可自定义)->链接(验证+准备(为静态变量分配内存并始化为默认值)+解析(将常量池内的符号引用换为直接引用))->初始化1次(主动引用类:实例化、读或写静态域(只初始化直接定义其的类,,当子类有覆盖父类的静态方法时既加载父类又加载子类,不含常量池里final型(值非域), 编译器计算不出常量的值时static final int a = math.PI;,VM启动时需先初始化一个要执行的主类(static void main(String[] args)))、执行静态方法;反射,如Class.forName(String className),Class.forName("className",false,SomeClass.class.getClassLoader()设为不加载), ClassLoader.loadClass(String 只加载并编译不初始化);初始化一个类,若其父类还没被初始化,先初始化其父类;接口初始化时,不要求其父接口初始化全部,除非使用到父接口)----到初始化过程都是线程安全的---->操作内存->卸载。
继承之间的调用顺序 父类static型 -> 子类static型 -> 父类普通成员初始化和初始化块 -> 父类构造方法 -> 子类普通成员初始化和初始化块 -> 子类构造方法。


多进程:1.静态成员和单例;2.跨进程的线程间同步问题;3.SharedPreferences的可靠性下降:SharedPreferences不支持并发读写;4.Application会多次创建:当一个组件跑在一个新的进程的时候,系统要在创建新的进程的同时分配独立的虚拟机,应用会重新启动一次。


序列化:1.静态成员变量属于类不属于对象,所以不参与序列化过程;2.声明为transient的成员变量不参与序列化;如果子类实现Serializable接口而父类未实现时,父类不会被序列化,但此时父类必须有个无参构造方法,否则会抛InvalidClassException。   RemoteView,Bundle(实现了Parcelable接口)、Bitmap等


BR:观察者模式:不建议另开线程。本地/全局,有/无序;

Normal broadcasts完全异步,所有的广播接收器几乎都会在同一时刻接收到,无法被截断。Ordered broadcasts有序/同步,同一时刻只会有一个广播接收器能够收到,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递,优先级( <intent-filter android:priority="100" >)高的广播接收器可以截断( abortBroadcast())正在传递的广播.
静态的开机(一般需APP自安装后启动过)自动接受广播。


监听充电状态:BatteryManager会发送一个包含充电状态的持续广播, 无需receiver, 可通过intent获取(BatteryManager.EXTRA_PLUGGED)相关数据:IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);Intent batteryStatus = context.registerReceiver(null, ifilter);

媒体库更新单个文件状态:MediaScannerConnection.scanFile(this , arrayOf(picFile.absolutePath) , arrayOf("image/jpeg"), { path, uri -> Log.i("cxmyDev", "onScanCompleted : " + path)}) 或 sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file);媒体库会在手机启动/SD更新媒体文件后需主动触发媒体库更新 // 通知卡插拔时主动全盘扫描,代价比较大,所以单个文件的刷新很有必要。



用户不选择默认启动的app Intent intent = new Intent(Intent.ACTION_SEND);Intent chooser = Intent.createChooser(intent, title);startActivity(chooser);  

intent.setClipData(); ClipboardManager cmb = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);   cmb.setText(content.trim()); cmb.setPrimaryClip( ClipData.newPlainText("text", content));

跳转到系统设置页Intent intent =new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package",getPackageName(), null));
try{
    startActivity(intent);
}catch(Exception exception) {
    exception.printStackTrace();
}

public void openFileBy3rd(final File f, final Context c) {
    MaterialDialog.Builder a = new MaterialDialog.Builder(c);
    a.title("open as");
    String[] items = {getString(c, R.string.text), getString(c, R.string.image), getString(c, R.string.video), getString(c, R.string.audio), getString(c, R.string.database), getString(c, R.string.other)};//
    Uri uri = Uri.fromFile(f);
    Intent intent = new Intent();
    intent.setAction(android.content.Intent.ACTION_VIEW);
    a.items(items).itemsCallback(new MaterialDialog.ListCallback() {        
        @Override public void onSelection(MaterialDialog materialDialog, View view, int i, CharSequence             charSequence) {   
                switch (i) {//intent.setDataAndType(uri, MimeTypes.getMimeType(new File(smbFile.getPath())));
                    case 0:     intent.setDataAndType(uri, "text/*");                    break;                             case 1:    intent.setDataAndType(uri, "image/*");                    break;                             case 2:    intent.setDataAndType(uri, "video/*");                    break;                             case 3:    intent.setDataAndType(uri, "audio/*");                    break;                             case 4:   //intent = new Intent(c, DbViewer.class);  intent.putExtra("path", f.getPath());   break;             
                    case 5:        intent.setDataAndType(uri, "*/*");                    break;      default:    throw new Exception("default");  }            
        try {  c.startActivity(intent);            } catch (Exception e) {  //openFileBy3rd(f, c);            }        }    });    
        try {        a.build().show();    } catch (Exception e) {        e.printStackTrace();    }}




public void onCreate() {
    super.onCreate();
    String processName = AppUtility.getProcessName(Process.myPid());
    if (getPackageName().equals(processName)) {
        // init
    }
  
}

/**
* 根据进程 ID 获取进程名
* @param pid
* @return
*/
public static String getProcessName(int pid){
  ActivityManager am = (ActivityManager) MainApplication.getContext().getSystemService(Context.ACTIVITY_SERVICE);
  List<RunningAppProcessInfo> processInfoList = am.getRunningAppProcesses();
  if (processInfoList == null) {
    return null;
  }
  for (RunningAppProcessInfo processInfo : processInfoList) {
    if (processInfo.pid == pid) {
      return processInfo.processName;
    }
  }
  return null;
}



public static String getProcessName() {
  try {
    File file = new File("/proc/" + android.os.Process.myPid() + "/" + "cmdline");
    BufferedReader mBufferedReader = new BufferedReader(new FileReader(file));
    String processName = mBufferedReader.readLine().trim();
    mBufferedReader.close();
    return processName;
  } catch (Exception e) {
    e.printStackTrace();
    return null;
  }
}



force-stop:所有广播都无法接收,除非带有标记FLAG_INCLUDE_STOPPED_PACKAGES的,系统默认的广播几乎都不带;不杀系统persistent进程;指定用户userId时,不杀其他用户空间的进程;


Environment.getDataDirectory() = /data
Environment.getDownloadCacheDirectory() = /cache
Environment.getExternalStorageDirectory() = /mnt/sdcard
Environment.getExternalStoragePublicDirectory(“test”) = /mnt/sdcard/test
Environment.getRootDirectory() = /system
getPackageCodePath() = /data/app/com.my.app-1.apk
getPackageResourcePath() = /data/app/com.my.app-1.apk
getCacheDir() = /data/data/com.my.app/cache
getDatabasePath(“test”) = /data/data/com.my.app/databases/test
getDir(“test”, Context.MODE_PRIVATE) = /data/data/com.my.app/app_test
getExternalCacheDir() = /mnt/sdcard/Android/data/com.my.app/cache
getExternalFilesDir(“test”) = /mnt/sdcard/Android/data/com.my.app/files/test
getExternalFilesDir(null) = /mnt/sdcard/Android/data/com.my.app/files
getFilesDir() = /data/data/com.my.app/files

Activity透明:<style name="TransparentActivity" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:colorBackgroundCacheHint">@null</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowContentOverlay">@null</item>
</styl



 
  
 
  
 
  
 
  
接口; 单一职责;开闭原则 循环(周期)指令

                                         以方法为编码单位

获取adapter,获取蓝牙打开状态(连接状态(连接的设备),搜索状态),(可多次发起(比如一次20s))搜索(异步,根据mac过滤蓝牙设备),(获取到目标设备关闭搜索),(配对)(先延迟50)连接,(或这里关闭搜索), 通信传输过程(连接状态)

BLE:18+,20+支持外设模式。Build.VERSION.SDK_INT<21(callbackofscan),mGatt.setCharacteristicNotification, mGatt.writeDescriptor. UUID(128bit:8-4-4-12 的16进制表示。预先定义了一些标准Service,如 Generic Access描述了设备连接的属性,Immediate Alert服务标识设备支持标准的即时提醒功能,Hear Rate表心率服务。直接显示 UUID的标示是自定义的,有 128 bit 的长的,也有 16 bit (需购买)的).Profile配置文件,把若干个相关的Service 组合在一起。一Service 包含若干个 Characteristic(含若干 Descriptor。而 Characteristic 定义了数值和操作(串行,操作请求队列化,权限:读、写、通知等)(若changed比writed先, 则write回调里的characteristic为notification里的) )。 BluetoothDevice.connectGatt() 或BluetoothGatt.connect()等建立BluetoothGatt连接时,只能一个设备在尝试连接(多个连接请求队列,连接失败了或超时可BluetoothGatt.disconnect(),类通信环节),否则连接失败阻塞且无连接回调。ScanFilter过滤出目标设备才上报,或延迟上报。

继承,包内(默认friendly/protected),类内

invalidate(,,,);

API Level24+将不再支持用户使用自签名的证书。

AdapterView:ListView是将所有的mActiveViews都移入了二级缓存mScrapViews,RecyclerView对每个ItemView修改标志位,区分是否重新bindView; RecyclerView:RecyclerView和Adapter翻译出的ViewHolder通信,LayoutManager通过Recycler管理ItemView

网络对TCP链路存在限制,TCP链路在长时间无数据流量时,会自动降低此链路的优先级直至强制断开此链路.可发送心跳来维持

VpnService(截获设备上所有发送和收到的数据包),VPN权限:VpnService.Builder.getFileDescriptor()

(AudioManager) getContext().getApplicationContext().getSystemService(Context.AUDIO_SERVICE);//注意不同上下文(系生命周期)不同作用域

context.getPackageManager.().getPackageInfo(packname,packageManager.GET_SIGNATURES);

if (BuildConfig.DEBUG) { StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectActivityLeaks()              

                .detectLeakedSqlLiteObjects()       
                .detectLeakedClosableObjects()      
                .detectLeakedRegistrationObjects()  //检测注册对象,API16+
                .penaltyLog()//在LogCat中打印
                .build());
    }super.onCreate(savedInstanceState); develoer optionsLeakcanaryMemory MonitorLint

gradle.properties:org.gradle.parallel=true;org.gradle.daemon=true;org.gradle.jvmargs=-Xms256m -Xmx1024m
dependencies {    compile('li.g.sdf:sdf:2.0', {       exclude group: 'com.android.support'    })    compile 'com.android.support:recyclerview-v7:需要的版本'} 

public static String add(String a, String b) {   return new BigDecimal(a).add(new BigDecimal(b)).toString();} //浮点数(科学计数法) 

随机整数'newRandom()'.nextInt((max- min)+1)+ min;'  

数字、字母和汉字混排占位问题:将数字和字母全角化

setCompoundDrawablesWithIntrinsicBounds()

imageView.setAlpha(float alpha);   imageView的最大高度需加imageView.setAdjustViewBounds();设置图片固定大小,并保持宽高比可:setAdjustViewBounds为true;
maxWidth、MaxHeight;layout_width和layout_height为wrap_content


WakeLock.acquire(long timeout);  

getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE)安全窗口,禁用系统截屏

ripple:?android:attr/selectableItemBackground 

String processName = getProcessName(this, android.os.Process.myPid()); if (!TextUtils.isEmpty(processName) && processName.equals(this.getPackageName())) {//判断进程名,如:保证只有主进程才初始化某些服务 

Resources.getSystem().getDisplayMetrics().density 获取屏幕密度
Html.fromHtml()     DateUtils.formatDateTime()输出相应格式化的时间或者日期 Build.VERSION_CODES        ArgbEvaluator.evaluate(float fraction, Object startValue, Object endValue)   根据一个起始颜色值和一个结束颜色值以及一个偏移量生成一个新的颜色,配合ValueAnimator.ofObject或者ViewPager.PageTransformer使用,或paint.setShader("LinearGradient")可颜色渐变;         Linkify.addLinks()为文本添加超链接            模块间有消息需要传递时,尝试LocalBroadcastManager替代Listener进行模块解耦。         PackageManager.getInstalledPackages尽量在子线程中使用。          飞行模式否Settings.System.getInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 1;          service经常因为重启之类的出现onStartCommand()中的Intent传递的参数为null, 把onStartCommand()中的返回值改成super.onStartCommand(intent, Service.START_REDELIVER_INTENT, startId);       
ListView中的信息被软键盘遮挡了,在设置listview的时候加上android:transcriptMode="normal"      listview的footerDividersEnabled和headerDividersEnabled方法可以设置listview的顶部和底部divide,但是必须保证设置了headview和footview, item中CheckBox等元素抢焦点导致item点击事件无法响应时,除了给对应的元素设置 focusable,还可在item根布局加android:descendantFocusability=”blocksDescendants”       includeFontPadding="false"        Listview clipToPadding=false配合paddingTop上滑时隐藏padding
view的isShown方法,只有当view本身以及它的所有vp都是visible时,isShown()才返回TRUE。而if(view.getVisibility() == View.VISIBLE)只是对view本身的可见性进行判断
android.media.ThumbnailUtils 获取媒体(图片、视频)缩略图;      
TextView的setTransformationMethod(TransformationMethod)方法,可用来实现“显示密码”
    

Android Device unique ID:

 Secure.getString(getContext().getContentResolver(), Secure.ANDROID_ID);
EditTxt.setImeOptions, 使用EditText弹出软键盘时,修改回车键的显示内容             

str.split("\\.");

      android:configChanges="orientation|locale|keyboardHidden"
android:alwaysRetainTaskState="true"           @android:drawable/dialog_holo_light_frame阴影灰图          cardview波纹型的选中效果android:foreground="?attr/selectableItemBackground"      


透明A:<style name="Theme.Transparent" parent="android:Theme">
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowIsFloating">true</item>
    <item name="android:backgroundDimEnabled">false</item>
  </style>

launch 3rd App: Intent launchIntent = getPackageManager().getLaunchIntentForPackage("com.package.address");
        try {
            startActivity(launchIntent);// 


        } catch (Exception e) {
            e.printStackTrace();
        }

public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels) {
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap
                .getHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(output);


        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);
        final float roundPx = pixels;


        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);


        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);


        return output;
    }

 URLEncoder.encode("apples oranges", "utf-8");


获取所有已安装应用信息,启动之等;Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> pkgAppsList = context.getPackageManager().queryIntentActivities( mainIntent, 0);


两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。
res/raw和assets的不同点:1)res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源id;assets文件夹下的文件访问时需AssetManager类。
2)res/raw不可以有目录结构,assets则可以有目录结构
3)读取文件资源:
res/raw下的,输入流:getResources().openRawResource(R.raw.filename); 
assets下的,  输入流: getAssets().open("filename"); asset里面的数据库文件大小不能超过1M。
SQLiteDatabase.openOrCreateDatabase(jhPath, null);     SQLiteDatabase.openDatabase

推流端(采集、预处理(水印,字幕等)、编码、预览,推流(SDK)),服务端(转码、录制、截图,,),播放器(拉流、解码、渲染)(大部分播放器都是拿到一个完成的 GOP ( Group of Pictures )后才能解码播放,基于 FFmpeg 移植的播放器需等待音画时间戳同步后才能播放(如果一个直播里边没有音频只有视频相当于要等待音频超时后才能播放画面)编码器将多张图像进行编码后生产成一段一段的 GOP, 解码器在播放时则是读取一段一段的 GOP 进行解码后读取画面再渲染显示)、互动系统(聊天室、礼物系统、赞)   编码器将多张图像进行编码后产生一段段GOP:一组连续的画面,由一张 I 帧和数张 B / P 帧(I 帧是内部编码帧(也称为关键帧,一个完整的画面),P 帧是前向预测帧(前向参考帧),B 帧是双向内插帧(双向参考帧), P 帧和 B 帧记录的是相对于 I 帧的变化。如果没有 I 帧,P 帧和 B 帧就无法被解码)组成,是视频图像编码器和解码器存取的基本单位,它的排列顺序将会一直重复到影像结束),播放时解码器读取一段段GOP进行解码后读取图像并进行渲染显示。 MediaRecorder,音视频很难同步,软编大占CPU。
视频文件(由图像和音频构成最基本的内容元素( Content );图像( Image )经过视频编码压缩格式处理(如H.264);音频 ( Audio )经过音频编码压缩格式处理(如 AAC);注明相应的元信息(Metadata);最后经过一遍容器(Container)封装打包(例如 MP4),构成一个完整的视频文件 ) ,其图像部分的数据是一组 GOP 的集合, 而单个 GOP 则是一组 I / P / B 帧图像的集合。Video 好比一个 “物体”,GOP 好比 “分子”,I / P / B 帧的图像则好比 “原子”。直播即把传输一个 “物体”,改成传输一个一个的 “原子”。



CP(跨进程阈值1M):onCreate比Application的先,其它回调于Binder线程池。权限管理(读写分离:权限控制(精确到表级:),URI控制()

ContentResolver cr =getContentResolver();
Cursor cursor = cr.query(Uri.parse("content://'authorities'"), null, null, null, null);  
cursor.close();

registerContentObserver
getContentResolver().notifyChange

SQLiteINTEGER PRIMARY KEY的字段只能存储64位整数,防SQL 注入:可用问号做参数替换符,因为用户输入直接受查询约束,未作为 SQL 处理,不解释为 SQL 语句的一部分  void insertWithOneTransaction(){

    SQLiteDatabase db=sqliteOpenHelper.getWritableDatabase();//get ReadableDatabase()方法先以读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,之后会继续尝试以只读方式
    db.beginTransaction();
    try{
        for(inti=0;i<100;i++){
            db. insert(tableName,null,contentValues);//若contentValues为null或者元素个数为0(否则第二个参数可传null), 由于Insert()要求必须添加一条除了主键之外其它字段为Null的记录,为了满足SQL语法的需要, insert语句必须给定一个字段名,如:insert into person(name) values(NULL),若不给定字段名 , insert语句就成了insert into person() values(),对于字段名,建议使用主键之外的字段,如果使用了INTEGER类型的主键字段,执行类似insert into person(personid) values(NULL)的insert语句后,该主键字段值也不会为NULL, delete("person", "personid<?", new String[]{"2"})、 update("person", contentValues, "personid=?", new String[]{"1"})、 query("person", new String[]{"personid,name,age"}, "name like ?", new String[]{"%极乐空间%"}, null, null, "personid desc", "1,2")、 ContentValues;     db. execSQL("insert into person(name, age)values(?,?)", new Object[]{"fg", 4})、delete、update和CREATE TABLE person (personid integer primary key autoincrement, name varchar(20))  之类更改行为的SQL语句; Cursor rawQuery("select * from person/ select * from person order by id desc/ select name from person group by name having count(*)>1").getInt(0); 获取第一列的值,第一列的索引从0开始,执行 select语句
        }
        db.setTransactionSuccessful(); //mark
    }catch(Exceptione){
            e.printStackTrace();
    }finally{ 
        db.endTransaction();
    }
}


NotificationManager mNotifyMgr =    (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNotifyMgr.notify(NOTIFICATIONS_ID, new Notification.Builder(this).setSmallIcon(R.drawable.notification_icon).setContentTitle("My notification").setContentText("功夫").setStyle(new NotificationCompat.BigTextStyle().bigText("16+")).setProgress(100, incr, false//(0, 0, true)表不明确的进度条).setFullScreenIntent(pendingIntent, false//浮动通知:或通知设置了高优先级且使用了铃声和振动).setDefaults(Notification.DEFAULT_ALL).addAction (R.drawable.ic_stat_dismiss, getString(R.string.dismiss), piDismiss).setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, ToActivity.class), 0)).build(16+);// getNotification()); NotificationCompat.Builder自动设置的默认值:priority: PRIORITY_DEFAULT  when: System.currentTimeMillis()  audio stream: STREAM_DEFAULT.  
取消通知:点击通知栏的清除;设置了 setAutoCancel() 或 FLAG_AUTO_CANCEL的通知,点击该通知时会清除它 ;NotificationManager 调用 cancel()清除指定ID的通知;NotificationManager.cancelAll()


适配:桌面快捷方式Intent i= new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);// 不重复创建
i.putExtra("duplicate", false);
i.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);
i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
sendBroadcast(i);

获取拍照图片:指定的拍照路径判空/异常捕获,图片名中不含空格等特殊符号;onActivityResult 中的 intent(data)判空;

UI:比例,fragment,方向,.9图(Drawable加上用于描述拉伸坐标的数组chunk,缩放Drawable时也必须更新chunk)。。。。;

权限:如7.0 Uri访问权限从FileProvider.getUriForFile():grantUriPermissions:true exported:false;   ACTION_OPEN_EXTERNAL_DIRECTORY 权限;  CONNECTIVITY_CHANGE 广播在后台时不再能接收到 ;不能发送或是接收新增图片(ACTION_NEW_PICTURE)和新增视频(ACTION_NEW_VIDEO) 的广播。在手机关屏且静止时,Doze 模式通过推迟 CPU 和网络操作,并一段时间后进一步限制 WakeLock、Alarm、GPS 和 Wi-Fi 扫描等。

6.0+的WifiManager获取MAC地址



动态(动态获取撤销权限, 不要持久化限获取状态,支持了运行时权限也要在Manifest声明因为市场应用会根据这个信息和硬件设备匹配决定应用是否在该设备上显示)权限之隐私与影响其它应用运行的权限:READ_PHONE_STATE用来获取deviceID并持久化,IMEI号码,这是很多统计依赖计算设备唯一ID的参考;WRITE_EXTERNAL_STORAGE / READ_EXTERNAL_STORAGE和外置存储(sdcard:19+)有关,如下载。


SYSTEM_ALERT_WINDOW悬浮窗,WRITE_SETTINGS 修改系统设置,用startActivityForResult启动授权界面:
请求SYSTEM_ALERT_WINDOW:
private  void requestAlertWindowPermission() {
    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
    intent.setData(Uri.parse("package:" + getPackageName()));
    startActivityForResult(intent, REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE) {
        if (Settings.canDrawOverlays(this)) {
          Log.i(LOGTAG, "onActivityResult granted");
        }
    }
}

请求WRITE_SETTINGS:
private void requestWriteSettings() {
    Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
    intent.setData(Uri.parse("package:" + getPackageName()));
    startActivityForResult(intent, REQUEST_CODE_WRITE_SETTINGS );
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE_WRITE_SETTINGS) {
        if (Settings.System.canWrite(this)) {
            Log.i(LOGTAG, "onActivityResult write settings granted" );
        }
    }
}



LaunchMode: 创建与销毁时 任务栈 (Stack<Activity>)的变化:实例复用

standard:: 栈内不复用
singleTop:栈顶复用。 从外界可能多次(从1个入口)跳转到一个界面( 接收通知/推送启动的): 结果页顶部含搜索框; 适合作为程序入口点,例如浏览器的主界面,不管从多少个应用启动浏览器,只会启动主界面一次;
singleTask: 栈内复用。同后者,点击Launcher启动图标被置栈顶;
singleInstance: 独占一个 任务栈。设置 taskAffinity=""可back到starter; singleInstance的 activity启动其它a,都无法获取返回值,如 onActivityResult收到RESULT_CANCELED。按返回键呈现其它最近栈,点击Launcher启动图标被置栈顶,闹铃提醒页,launcher主页

默认一个应用的所有Activity都是具有相同的affinity,都从application中继承,application的affinity默认就是包名。affinity对Activity来说,就像id标记所属taskstack。应用场合:根据affinity重新为Activity选择合适的宿主Taskstack;TaskAffinity属性主要和singleTask(它是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中)模式或allowTaskReparenting属性配对使用。

FLAG_ACTIVITY_

(可视)对称周期:Activity的 onSave InstanceState(onStop()前) , onRestore InstanceState(总要调用onRestoreInstanceState()方法的父类实现,onStart() 后,有数据时回调)

onPause()释放CPU,止动画或其他正在运行可非视觉需要的操作,轻量存储,.释放系统资源如sensors (比如GPS),camera等

onStop():完全不可见

onStart()验证某些必须的系统特性(如 locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))是否可用等

onDestroy:如:设置个flag,用来判断用户是否第一次进入这个A,在退出时保存这个flag。

没有配置Intent-filter的action属性,exported默认为false


View.getContext,返回当前View对象的Context对象,通常是当前展示的Activity对象;Activity.getApplicationContext(/getApplication())获取当前Activity所在的进程的Context对象;ContextWrapper.getBaseContext()获取一个ContextWrapper装饰之前的Context


锁屏:IntentFilter mScreenOffFilter = new IntentFilter();mScreenOffFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(receiver, mScreenOffFilter);  private BroadcastReceiver receiver = new BroadcastReceiver() {    @SuppressWarnings("deprecation")    @Override
    public void onReceive(Context context, Intent intent) {        if (intent.getAction().equals(NOTIFY_SCREEN_OFF)) {
            Intent mLockIntent = new Intent(context, A_Lock.class);
            mLockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK //比如在Service启动A时,没它则出现“Calling startActivity() from outside of an Activity”异常,也需要在AndroidManifest中声明A的taskAffinity,即新task名
                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);//避免在最近使用程序列表出现A
            startActivity(mLockIntent);
        }

       if(intent.getAction().equals(Intent.ACTION_USER_PRESENT)) {    if (VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {        if ((KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE).isKeyguardSecure()) {//设置了锁屏密码finish A
        }
    }
      } 

};

A_Lock.onCreate(): getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);//去掉系统锁屏页
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);//使Activity在锁屏时仍然能够显示     void setFullScreen(){ //沉浸式 可能需在onWindowFocusChanged()再设置一遍

    getWindow().getDecorView().setSystemUiVisibility(
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE  //使View不因为SystemUI的变化重布局
    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION  //使导航栏浮在屏幕最上层,不占据屏幕空间
    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN  //使状态栏浮在屏幕上层
    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY  //在隐藏的NavigationBar被呼出时,使bar在无相关操作的情况下自动再次隐藏
    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION  //隐藏导航栏
);}
界面的内容不被上拉到状态栏,需在A的最外层Layout中将fitsSystemWindows设为true  <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>



Back键和Menu键可以通过onKeyDown()屏蔽:public boolean onKeyDown(int keyCode, KeyEvent event) {    int key = event.getKeyCode();    switch (key) {        case KeyEvent.KEYCODE_BACK: {            return true;
        }        case KeyEvent.KEYCODE_MENU:{            return true;
        }
    }    return super.onKeyDown(keyCode, event);
}Home键与Recent键的点击事件是在framework层,因此onKeyDown与dispatchKeyEvent捕获不了


透明栏只是能够改变状态栏和导航栏的颜色:
4.4以上5.0以下:if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
    Window window = getWindow();
    window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
5.0及以上:if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    Window window = getWindow();
    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    window.getDecorView()
            .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
    window.setStatusBarColor(0);
}


有的rom,锁屏页罩在系统锁屏页上时(有锁屏密码),须要划开自定义锁屏页,在系统锁屏页上才能指纹解锁。 识别指纹并解锁:  public boolean isFingerprintAuthAvailable() {    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        mKeyguardManager = (KeyguardManager) getSystemService(Activity.KEYGUARD_SERVICE);        if(!mKeyguardManager.isKeyguardSecure()){  //没设置密码锁屏          return false;
        }        if (checkSelfPermission(Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED) {
            mFingerprintManager = (FingerprintManager) getSystemService(Activity.FINGERPRINT_SERVICE);
            mCancellationSignal = new CancellationSignal();            return  mFingerprintManager.isHardwareDetected()&&mFingerprintManager.hasEnrolledFingerprints();
        }else{            return false;
        }
    }else{        return false;
    }
private void startFingerPrintListening() {    if (!isFingerprintAuthAvailable()) {        return;
    } else {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES. M) {            if (checkSelfPermission(Manifest.permission. USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED) {
                mFingerprintManager.authenticate(null, mCancellationSignal, 0, new FingerprintManager.AuthenticationCallback() {   //第一个crypto参数表Android6.0中crypto objects的wrapper class,通过该对象使authenticate更安全
                @Override
                    public void onAuthenticationError(int errorCode, CharSequence errString) {   //指纹匹配连续失败后(几十秒后才能继续匹配)                    super.onAuthenticationError(errorCode, errString);
                    }                    @Override
                    public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {                        super.onAuthenticationSucceeded(result);
                        finish();
                    }                    @Override
                    public void onAuthenticationFailed() {                        super.onAuthenticationFailed();
                    }
                }, null);                return;
            }
        }
    }
}    <uses-permission android:name="android.permission.USE_FINGERPRINT"/>



F(最好可被复用且不直接操纵其他F)onAttatch-onCreate-onCreateView-onActivityCreated-onStart-onResume-Fragment已激活,返回键或被添加到返回栈被移除/替换-onPause-onStop-onDestroyView-onDestroy-onDetach-被销毁,从返回栈中回到上个碎片:onDestroyView-onCreateView.进入停止状态的可能在系统内存不足的时候被回收,保存的数据在onCreate() onCreateView()和onActivityCreated()都可重新得到。可用同一个 FragmentTransaction进行次 fragmentTransaction在执行fragment事务时,如移除或替换,经常要适当地让用户可以向后导航与"撤销"这次改变,须在FragmentTransaction提交前调用addToBackStack()。         a.findFragmentById/Tag/()获取fragment实例, f.getActivity()获取。 hide、show时,并不会调用Fragment的生命周期回调。newInstance(int args)(instance.setArguments(new Bundle)().put("param", args);).  F 里装载 F 时用getChildFragmentManager() 方法获取 FragmentManager 对象.  FragmentTransaction:add(); remove();replace(不保留 F状态) :调用 remove() 和 add() ;hide(通过onHiddenChanged() ) 和 show(可监听);addToBackStack():返回键时界面回到当前事物状态;commit().  setUserVisibleHint() 常在 ViewPager 和 Fragment 组合的 FragmentPagerAdapter 中,ViewPager 滑动时便是通过这个方法改变 Fragment 的状态,可实现 F 懒加载

复用: private void showFragment(String tag, Class<? extends Fragment> modelFragment, int layoutId) {
    Fragment f = fm.findFragmentByTag(tag);
    if (f == null) {
     
        try {
            f = modelFragment.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (curFragment == null) {
    

            fm.beginTransaction()
                    .add(layoutId, f, tag)
                    .commit();
        } else {
    
            fm.beginTransaction()
                    .hide(curFragment)
                    .add(layoutId, f, tag)
                    .commit();
        }
    } else { // 
        if (curFragment == f) return;

if (curFragment !=null)
        fm.beginTransaction()
                .hide(curFragment)
                .show(f)
                .commit();

else

 fm.beginTransaction()
              
                .show(f)
                .commit();//commitAllowingStateLoss();

    }    curFragment = f;}
 
 

 类似资料: