美文网首页
android规范

android规范

作者: ccccccal | 来源:发表于2018-03-05 23:42 被阅读82次
    1. Activity 间的数据通信,对于数据量比较大的,避免使用 Intent + Parcelable
      的方式,可以考虑 EventBus 等替代方案,以免造成 TransactionTooLargeException

    2. activity隐式跳转,在发出 Intent 之前必须通过 resolveActivity检查,避免找不到合适的调用组件,造成 ActivityNotFoundException 的异常

    public void viewUrl(String url, String mimeType) {
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(Uri.parse(url), mimeType);
    if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_
    ONLY) != null) {
    try {
    startActivity(intent);
    } catch (ActivityNotFoundException e) {
    if (Config.LOGD) {
    Log.d(LOGTAG, "activity not found for " + mimeType + " over " +
    Uri.parse(url). getScheme(), e);
    
    
    1. 避免在 Service#onStartCommand()/onBind()方法中执行耗时操作,如果确实有需求,应改用 IntentService 或采用其他异步机制完成

    2. 避免在 BroadcastReceiver#onReceive()中执行耗时操作,如果有耗时工作,应该创建 IntentService 完成,而不应该在 BroadcastReceiver 内创建子线程去做。
      说明:
      由于该方法是在主线程执行,如果执行耗时操作会导致 UI 不流畅。可以使用IntentService 、 创 建 HandlerThread 或 者 调 用 Context#registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)方法等方式,在其他 Wroker 线程执行 onReceive 方法。 BroadcastReceiver#onReceive()方法耗时超过 10 秒钟,可能会被系统杀死

    IntentFilter filter = new IntentFilter();
    filter.addAction(LOGIN_SUCCESS);
    this.registerReceiver(mBroadcastReceiver, filter);
    mBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
    Intent userHomeIntent = new Intent();
    userHomeIntent.setClass(this, UseHomeActivity.class);
    this.startActivity(userHomeIntent);
    }
    };
    
    1. 添 加 Fragment 时 , 确 保 FragmentTransaction#commit() 在
      Activity#onPostResume()或FragmentActivity#onResumeFragments()内调用。
      不要随意使用FragmentTransaction#commitAllowingStateLoss()来代替,任何
      commitAllowingStateLoss()的使用必须经过 code review,确保无负面影响。

    说明如下:

    Activity 可 能 因 为 各 种 原 因 被 销 毁 , Android 支 持 页 面 被 销 毁 前 通 过
    Activity#onSaveInstanceState() 保 存 自 己 的 状 态 。 但 如 果
    FragmentTransaction.commit()发生在 Activity 状态保存之后,就会导致 Activity 重
    建、恢复状态时无法还原页面状态,从而可能出错。为了避免给用户造成不好的体
    验,系统会抛出 IllegalStateExceptionStateLoss 异常。推荐的做法是在 Activity 的
    onPostResume() 或 onResumeFragments() ( 对 FragmentActivity ) 里 执 行
    FragmentTransaction.commit(),如有必要也可在 onCreate()里执行。不要随意改用
    FragmentTransaction.commitAllowingStateLoss() 或 者 直 接 使 用 try-catch 避 免
    crash,这不是问题的根本解决之道,当且仅当你确认 Activity 重建、恢复状态时,
    本次 commit 丢失不会造成影响时才可这么做

    1. 对于只用于应用内的广播,优先使用 LocalBroadcastManager 来进行注册
      和发送,LocalBroadcastManager 安全性更好,同时拥有更高的运行效率。
      说明:
      对于使用 Context#sendBroadcast()等方法发送全局广播的代码进行提示。如果该广
      播仅用于应用内,则可以使用 LocalBroadcastManager 来避免广播泄漏以及广播被
      拦截等安全问题,同时相对全局广播本地广播的更高效

    2. 使用 Toast 时,建议定义一个全局的 Toast 对象,这样可以避免连续显示
      Toast 时不能取消上一次 Toast 消息的情况(如果你有连续弹出 Toast 的情况,避免
      使用 Toast.makeText)

    3. 布局中不得不使用 ViewGroup 多重嵌套时,不要使用 LinearLayout 嵌套,
      改用 RelativeLayout,可以有效降低嵌套数。
      说明:
      Android 应用页面上任何一个 View 都需要经过 measure、 layout、 draw 三个步骤
      才能被正确的渲染。从 xml layout 的顶部节点开始进行 measure,每个子节点都需
      要向自己的父节点提供自己的尺寸来决定展示的位置,在此过程中可能还会重新
      measure(由此可能导致 measure 的时间消耗为原来的 2-3 倍)。节点所处位置越
      深,套嵌带来的 measure 越多,计算就会越费时。这就是为什么扁平的 View 结构
      会性能更好。
      同时,页面拥上的 View 越多,measure、 layout、 draw 所花费的时间就越久。要缩
      短这个时间,关键是保持 View 的树形结构尽量扁平,而且要移除所有不需要渲染的
      View。理想情况下,总共的 measure,layout,draw 时间应该被很好的控制在 16ms
      以内,以保证滑动屏幕时 UI 的流畅。
      要找到那些多余的 View(增加渲染延迟的 view),可以用 Android Studio Monitor
      里的 Hierarachy Viewer 工具,可视化的查看所有的 view。

    4. 在 Application 的业务初始化代码加入进程判断,确保只在自己需要的进程
      初始化。特别是后台进程减少不必要的业务初始化。

    5. 不要通过 Intent 在 Android 基础组件之间传递大数据(binder transaction
      缓存为 1MB),可能导致 OOM。

    6. 新建线程时,必须通过线程池提供(AsyncTask 或者 ThreadPoolExecutor
      或者其他形式自定义的线程池),不允许在应用中自行显式创建线程。
      说明:
      使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解
      决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致
      消耗完内存或者“过度切换”的问题。 另外创建匿名线程不便于后续的资源使用分析,
      对性能分析等会造成困扰。

    int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
    int KEEP_ALIVE_TIME = 1;
    TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
    BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
    ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES,
    NUMBER_OF_CORES*2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, taskQueue,
    new BackgroundThreadFactory(), new DefaultRejectedExecutionHandler());
    //执行任务
    executorService.execute(new Runnnable() {
    ...
    });
    
    1. 线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方
      式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险Executors 返回的线程池对象的弊端如下:
    1. FixedThreadPool 和 SingleThreadPool : 允 许 的 请 求 队 列 长 度 为
      Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM;
    2. CachedThreadPool 和 ScheduledThreadPool : 允 许 的 创 建 线 程 数 量 为
      Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
      正例:
    int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
    int KEEP_ALIVE_TIME = 1;
    TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
    BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
    ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES,
    NUMBER_OF_CORES*2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT,
    taskQueue, new BackgroundThreadFactory(), new DefaultRejectedExecutionHandler());
    
    1. 禁 止 在 多 进 程 之 间 用 SharedPreferences 共 享 数 据 , 虽 然 可 以
      (MODE_MULTI_PROCESS),但官方已不推荐

    2. ThreadPoolExecutor 设置线程存活时间(setKeepAliveTime),确保空闲时
      线程能被释放

    3. 谨慎使用 Android 的多进程,多进程虽然能够降低主进程的内存压力,但会
      遇到如下问题:

    1. 不能实现完全退出所有 Activity 的功能;
    2. 首次进入新启动进程的页面时会有延时的现象(有可能黑屏、白屏几秒,是白
      屏还是黑屏和新 Activity 的主题有关);
    3. 应用内多进程时,Application 实例化多次,需要考虑各个模块是否都需要在所
      有进程中初始化;
    4. 多进程间通过 SharedPreferences 共享数据时不稳定

    16.应用间共享文件时,不要通过放宽文件系统权限的方式去实现,而应使用
    FileProvider。

    1. SharedPreference 提 交 数 据 时 , 尽 量 使 用 Editor#apply() , 而 非
      Editor#commit()。一般来讲,仅当需要确定提交结果,并据此有后续操作时,才使
      用 Editor#commit()。
      说明:
      SharedPreference 相关修改使用 apply 方法进行提交会先写入内存,然后异步写入
      磁盘,commit 方法是直接写入磁盘。如果频繁操作的话 apply 的性能会优于 commit,apply 会将最后修改内容写入磁盘。但是如果希望立刻获取存储操作的结果,并据此
      做相应的其他操作,应当使用 commit。

    2. 多线程操作写入数据库时,需要使用事务,以免出现同步问题。
      说明:
      Android 的通过 SQLiteOpenHelper 获取数据库 SQLiteDatabase 实例,Helper 中会
      自动缓存已经打开的 SQLiteDatabase 实例,单个 App 中应使用 SQLiteOpenHelper
      的单例模式确保数据库连接唯一。由于 SQLite 自身是数据库级锁,单个数据库操作
      是保证线程安全的(不能同时写入),transaction 时一次原子操作,因此处于事务中
      的操作是线程安全的。
      若同时打开多个数据库连接,并通过多线程写入数据库,会导致数据库异常,提示
      数据库已被锁住

    public void insertUserPhoto(SQLiteDatabase db, String userId, String content) {
    ContentValues cv = new ContentValues();
    cv.put("userId", userId);
    cv.put("content", content);
    db.beginTransaction();
    try {
    db.insert(TUserPhoto, null, cv);
    // 其他操作
    db.setTransactionSuccessful();
    } catch (Exception e) {
    // TODO
    } finally {
    db.endTransaction();
    }
    }
    
    1. 大数据写入数据库时,请使用事务或其他能够提高 I/O 效率的机制,保证执行速度。

    2. 加载大图片或者一次性加载多张图片,应该在异步线程中进行。图片的加载,涉及到 IO 操作,以及 CPU 密集操作,很可能引起卡顿

    3. png 图片使用 tinypng 或者类似工具压缩处理,减少包体积。

    4. 在 Activity.onPause()或 Activity.onStop()回调中,关闭当前 activity 正在执
      行的的动画。

    相关文章

      网友评论

          本文标题:android规范

          本文链接:https://www.haomeiwen.com/subject/egukfftx.html