zengjingfang / AndroidBox

Android开发知识、经验、资料等总结,作为个人的开发知识体系
Apache License 2.0
16 stars 3 forks source link

《阿里巴巴Android开发手册》拆解记录 #14

Open zengjingfang opened 6 years ago

zengjingfang commented 6 years ago

1、Activity#onSaveInstanceState() 什么时候调用

image

通过源码追踪:

    private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
        r.state = new Bundle();
        r.state.setAllowFds(false);
        if (r.isPersistable()) {
            r.persistentState = new PersistableBundle();
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
                    r.persistentState);
        } else {
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
        }
    }

查到源头,真正触发的有三处:

final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
            boolean saveState) {     
        try {
            // Next have the activity save its current state and managed dialogs...
            if (!r.activity.mFinished && saveState) {
              // 此处回调了OnSaveInstanceState
                callCallActivityOnSaveInstanceState(r);
            }
            // Now we are idle.
            r.activity.mCalled = false;
           // 这里回调了生命周期中的 OnPause
            mInstrumentation.callActivityOnPause(r.activity);        
            }
        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {

            }
        }
        r.paused = true;

        return !r.activity.mFinished && saveState ? r.state : null;
    }
 private void handleRelaunchActivity(ActivityClientRecord tmp) {
        // If we are getting ready to gc after going to the background, well
       // 省略代码
        // If there was a pending configuration change, execute it first.
        if (changedConfig != null) {
            mCurDefaultDisplayDpi = changedConfig.densityDpi;
            updateDefaultDensity();
            handleConfigurationChanged(changedConfig, null);
        }

        ActivityClientRecord r = mActivities.get(tmp.token);
        // Need to ensure state is saved.
        if (!r.paused) {
            performPauseActivity(r.token, false, r.isPreHoneycomb());
        }
        if (r.state == null && !r.stopped && !r.isPreHoneycomb()) {
            // 此处回调了OnSaveInstanceState
            callCallActivityOnSaveInstanceState(r);
        }

        handleDestroyActivity(r.token, false, configChanges, true);

        r.activity = null;
        r.window = null;
        r.hideForNow = false;
        r.nextIdle = null;

        handleLaunchActivity(r, currentIntent);
    }

由源码可知,Activity#onSaveInstanceState()在三种情况下会被回调

在上述情况中,如果满足条件则回调callCallActivityOnSaveInstanceState

实际使用场景会被回调的情况如下

总之,就是你的操作并不确定要销毁当前的activity,但是由于系统的机制比如回收掉当前的activity,当再次恢复时就可能存在无法正常恢复的风险。通过在OnSaveInstanceState回调中保存临时数据,这样呢在恢复时会走onCreate(Bundle savedInstanceState),把临时保存的数据拿出来。如果你是非常确定要退出当前的activity,比如按下back键,那就不会回调OnSaveInstanceState。

还需要知道的是,系统默认实现了部分activity中的某些状态数据,比如UI控件的状态,比如EditText控件会自动保存和恢复输入的数据,前提是有为这个控件制定唯一的id,即android:id;

参考链接:

zengjingfang commented 6 years ago

2、如果有耗时操作,为什么不应该在BroadcastReceiver内创建子线程去做

image

原因是:BroadcastReceiver正常结束后,进程会被系统优先杀死,无法保证安全的执行完子线程的任务

BroadcastReceiver的onReceive()方法执行完成后,BroadcastReceiver的实例就会被销毁。如果onReceive()方法在10s内没有执行完毕,Android会认为改程序无响应。所以在BroadcastReceiver里不能做一些比较耗时的操作,否则会弹出“Application NoResponse”对话框。

这里不能使用子线程来解决 ,因为BroadcastReceiver的生命周期很短,子线程可能还没有结束BroadcastReceiver就先结束了。BroadcastReceiver一旦结束,此时它所在的进程很容易在系统需要内存时被优先杀死,因为它属于空进程。 【内容来自Broadcast Receiver开启服务而不是子线程处理耗时操作

zengjingfang commented 6 years ago

3、广播如何被恶意丢弃和Intent拦截,向广播结果塞入恶意数据

image

根据提供的参考链接,找到原文:

Furthermore, if the broadcast is an ordered broadcast then a malicious app could register itself with a high priority so as to receive the broadcast first. Then, it could either cancel the broadcast preventing it from being propagated further, thereby causing a denial of service, or it could inject a malicious data result into the broadcast that is ultimately returned to the sender.

避免的方式有三种

Therefore, receivers of broadcast intents should be restricted. One way to restrict receivers is to use an explicit intent. An explicit intent can specify a component (using setComponent(ComponentName)) or a class (using setClass(Context, Class)) so that only the specified component or class can resolve the intent.

It is also possible to restrict the receivers of intents by using permissions, as described below. Alternatively, starting with the Android version ICE_CREAM_SANDWICH (Android API version 4.0), you can also safely restrict the broadcast to a single application by using Intent.setPackage().

Yet another approach is to use the LocalBroadcastManager class. Using this class, the intent broadcast never goes outside of the current process. According to the Android API Reference, LocalBroadcastManager has a number of advantages over Context.sendBroadcast(Intent):

zengjingfang commented 6 years ago

4、全局的Toast为什么可以避免连续弹出不能取消上次的

image

zengjingfang commented 6 years ago

5、ViewHolder做缓存getView方法内的子控件要显性的设置相关属性

image

zengjingfang commented 6 years ago

6、文本单位使用dp?

image

zengjingfang commented 6 years ago

7、SharedPreferences多进程共享存在什么问题?

image

zengjingfang commented 6 years ago

8、SharedPreference 提交时为何要尽量使用apply,而不是commt?

image

zengjingfang commented 6 years ago

9、操作数据库时,为什么使用事务机制可以保证线程安全,并且效率要高?

image image