android tv 信源不一致问题

项目中 由于用户投诉, 新增了一个需求, 用户在看tv的时候, 关机重启后 仍然希望是进入tv界面下,而我们现在是不管上次关机是在tv还是launcher下, 重启以后都是进入launcher下。 想想这个需求是很正常的, 用户买回电视主要还是看tv的, 每次重启后都是进入launcher, 每次都需要手动切换到tv下, 用户不投诉才怪。

解决办法也比较简单,

一,

在系统属性中增加了一个属性, persist.sys.bootfromui,  进入launcher的时候 将persist.sys.bootfromui 设为true, 进入tv的时候将persist.sys.bootfromui设为false。 在ActivitStack.java 增加的代码如下:

   final boolean resumeTopActivityLocked(ActivityRecord prev) {
1288         // Find the first activity that is not finishing.
1289         ActivityRecord next = topRunningActivityLocked(null);                                                                                    
1290 
1291         // Remember how we'll process this pause/resume situation, and ensure
1292         // that the state is reset however we wind up proceeding.
1293         final boolean userLeaving = mUserLeaving;
1294         mUserLeaving = false;
1295 
1296         if (next == null) {
1297             // There are no more activities!  Let's just start up the
1298             // Launcher...
1299             if (mMainStack) {
1300                 return mService.startHomeActivityLocked();
1301             }
1302         }
1303         
1304         Slog.d(TAG,"resumeTopActivty");
1305         Slog.d(TAG, "------------liuwei-----------resumeTopActivty");
1306         if(next.packageName.equals("com.tcl.tv") || next.packageName.equals("com.tcl.common.bootsetwizard")){
1307             Slog.d(TAG, "set persist.sys.bootformui false ");
1308             SystemProperties.set("persist.sys.bootfromui","false");
1309         }else{
1310             Slog.d(TAG, "set persist.sys.bootformui true ");
1311             SystemProperties.set("persist.sys.bootfromui","true");
1312         }

二,每次在开机启动的时候根据 这个属性来判断是进入launcher 还是 TV,  在ActivityManagerService.java改动的代码如下

        Slog.d(TAG, "----------liuwei------------startHomeActivityLocked");
       boolean isStartUi = "true".equals(SystemProperties.get("persist.sys.bootfromui","false"));
        if(isStartUi)</span>
        {
           Slog.d(TAG, "----------zhuxiaolin------------isStartUi is true");

        Intent intent = new Intent(
            mTopAction,
            mTopData != null ? Uri.parse(mTopData) : null);
        intent.setComponent(mTopComponent);
        if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
            intent.addCategory(Intent.CATEGORY_HOME);
        }
        ActivityInfo aInfo =
            intent.resolveActivityInfo(mContext.getPackageManager(),
                    STOCK_PM_FLAGS);
        if (aInfo != null) {
            intent.setComponent(new ComponentName(
                    aInfo.applicationInfo.packageName, aInfo.name));
            // Don't do this if the home app is currently being
            // instrumented.
            ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                    aInfo.applicationInfo.uid);
            if (app == null || app.instrumentationClass == null) {
                intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
                mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
                        null, null, 0, 0, 0, false, false, null);
            }
        }
        }else{
            Slog.d(TAG, "----------liuwei------------isStartUi is false");
            Intent mIntent = new Intent();
            //mIntent.setClassName("com.tcl.cyberui","com.tcl.cyberui.MainActivity");
            mIntent.setClassName("com.tcl.tv","com.tcl.tv.TVActivity");
            mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            Slog.d(TAG, "----------zhuxiaolin------------ mContext.startActivity(mIntent)");
            mContext.startActivity(mIntent);
       }
 
三 引起的信源不一致问题
开机记忆TV的问题解决了。但是后来测试却发现一个更严重的问题, 有时候开机前TV的信源是DTV, 开机后的信源却是ATV或其他。经过多次测试总结出了规律, 如果第一次关机前是在launcher 状态下, 第二次关机前是tv状态下,就会出现信源就会出现不一致的情况。也就是说只要连续两次关机前的状态不一致(一次是launcher, 一次是TV)就必定会出现这个问题。如果两次关机前都是在luancher下 或都是在TV下那么就不会出现信源不一致的问题。

有了这个规律,那么就大胆猜测是由于luancher启动的时候读取信源的位置和 tv启动读取信源的位置不一致。

关键就在与这个位置不知道保存在哪里, 因为底层代码是厂商的我们看不到,很可能是保存在某个数据库里的。


后来不停的测试,加打印log, 发现不管是launcher先启动,还是TV先启动, 都会调用TVCommonNative.getDefault的getService来获取TVCommonService,

 static public ITVCommon getDefault(Context context) {
 155         if (mContext == null && context !=null) {
 156             mContext = context.getApplicationContext();
 157         }
 158 
 159         if (gDefault != null) {
 160             return gDefault;
 161         }
 162        IBinder b = getService(TVCOMMON);                                                                                                        
 163         if (b == null) {
 164             b = new TVCommonService();
 165             TVLog(TVCOMMON
 166                     + "service is not exit in ServiceManager, try to add it to ServiceManager");
 167             addService(TVCOMMON, b);</span>
 168         }

看到没, 第一次肯定都是走的红色代码,因为第一次都是空,先创建TVCommonService,在addService到ServiceManager。

再看看TVCommonService的构造函数:

  88     TVCommonService() {
  89 
  90         super();
  91         tvMgr = TVManager.getInstance(getContext());
  92         rawChSrv = (ChannelService) tvMgr
  93                 .getService(ChannelService.ChannelServiceName);
  94         cfgSrv = (ConfigService)tvMgr
  95                 .getService(ConfigService.ConfigServiceName);
  96         inputSrv = (InputService) tvMgr
  97                 .getService(InputService.InputServiceName);
  98         brdcstSrv = (BroadcastService) tvMgr
  99                 .getService(BroadcastService.BrdcstServiceName);
 100 
 101         mLooper = Looper.getMainLooper();
 102         mQueue = Looper.myQueue();
 103 
 104         mQueue.addIdleHandler(this);
 105         handler = new Handler(mLooper, this);
 106        <span style="color:#CC0000;"> storage = TVStorage.getInstance(getContext());</span>
 107         configurer = TVConfigurer.getInstance(getContext());

看到了,有个存储的东东叫TVStorage, 而且这个TVStorage是和context相关的!!!!!

也就是说, tv先启动的时候,这个TvStorage的context是tv 的, launcher先启动的时候context是launcher, 则也就导致了tv先启动和 luancher先启动 读取,存放信源的地方是不一致的。

我们来看看TVStorage的代码:

        private TVStorage(Context context) {
               pref = context.getSharedPreferences(TVCM_TABLE, Context.MODE_PRIVATE);
               pref = tvContext.getSharedPreferences(TVCM_TABLE, Context.MODE_WORLD_WRITEABLE);
                editor = pref.edit();
        }

36
 37     public String getString(String key, String def) {
 38         return pref.getString(key, def);
 39     }
 40
 41     public void setString(String key, String val) {
 42         editor.putString(key, val);
 43         editor.commit();
 44
 45     }

看到了吗,是放到context 的preference下, 熟悉java上层应用的人都熟悉吧。

我就是不知道preference这个东西,所以卡到context那里 不知道往下怎么跟踪了。 追到这个地方是另外一个同事发现的。


四 。 知道了原因 代码修改如下

  private TVStorage(Context context) {
 15         //改为指定用TV的context读写属性,所以要求第一个调用TV控制接口的应用有systemuid
 16         Context tvContext;
 17         try {
 18             tvContext = context.createPackageContext("com.tcl.tv", Context.CONTEXT_IGNORE_SECURITY);
 19         } catch (NameNotFoundException e) {
 20             // TODO Auto-generated catch block
 21             e.printStackTrace();
 22             tvContext = context;                                                                                                                  
 23         }
 24         pref = tvContext.getSharedPreferences(TVCM_TABLE, Context.MODE_WORLD_WRITEABLE);
 25         editor = pref.edit();
 26     }

把context写死到tv context下了,即不管是tv先启动,还是launcher先启动, storage都是读取,存储到tv的preference下了。

五  。其实我对这个该法是有意见的,在TVCommonService里我们可以看到, 不仅仅是TVstorage要用到context, 其他还有地方用到context, 我们只修改了storage的context, 那么其他地方的没有改,有很大的可能会出现问题。如果都修改, 那么对代码的改动太大了, 很可能造成更多的问题。  出现这个问题的本质 是因为TVCommonService是和context相关的, 只要每次开机add service的context不固定, 就会出先问题。 我比较推荐的方法 还是 永远都是launcher先启动,永远都用launcher的context去add TVCommonService, 启动luancher后,由launcher去判断 persist.sys.bootfromui, 由launcher去决定起TV或是不起。 毕竟以前一直都是先起launcher的,而这样也最稳定, 改动也很小。 可惜launcher不是我负责的, 做luancher的不想这么改,暂时修改Storage ,写死用tv的context。在我看来,这种写法实在不好, 除了刚才我提到的原因,底层的东西怎么能写死上层的package name呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值