<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ?不錯!實現(xiàn)動態(tài)切換應(yīng)用圖標

          共 21204字,需瀏覽 43分鐘

           ·

          2022-06-30 23:05

           安卓進階漲薪訓(xùn)練營,讓一部分人先進大廠


          大家好,我是皇叔,最近開了一個安卓進階漲薪訓(xùn)練營,可以幫助大家突破技術(shù)&職場瓶頸,從而度過難關(guān),進入心儀的公司。


          詳情見文章:沒錯!皇叔開了個訓(xùn)練營


          作者:下午吃早餐同學(xué)
          https://juejin.cn/post/7018100275336462372


          注:經(jīng)常聽到大家討論類似的需求,懷疑大廠是不是用了此方案,據(jù)我個人了解,多數(shù)頭部 app 其實都是發(fā)版來更新節(jié)假日的 icon。


          當然本方案也是一種可選的方案,以前我也調(diào)研過,存在問題和作者所述差不多,此外原文鏈接作者也回復(fù)了很多疑問,可以同時了解。


          開始了解吧...


          注意:我公司已采用此方案投入生產(chǎn),此方案存在缺陷并非完美方案,采用前請評估是否接受缺陷,具體缺陷見文末。

          1.效果圖


          2.產(chǎn)品需求

          市面上很多App能根據(jù)特定活動,動態(tài)切換應(yīng)用圖標達到宣傳目的,例如淘寶雙十一,國慶節(jié)等等。那么我們怎樣才能在不發(fā)新版本的情況下,動態(tài)切換應(yīng)用圖標呢?

          3.具體方案

          1.圖標更換:在AndroidManifest設(shè)置應(yīng)用入口Activity的別名,然后通過setComponentEnabledSetting動態(tài)啟用或禁用別名進行圖標切換。


          2.控制圖標顯示:冷啟動App時,調(diào)用接口判斷是否需要切換icon。


          3.觸發(fā)時機:監(jiān)聽App前后臺切換,當App處于后臺時切換圖標,使得用戶無感知。

          4.代碼實現(xiàn)

          在AndroidManifest.xml中給入口Activity設(shè)置activity-alias


          <application
              android:name=".MyApplication"
              android:allowBackup="true"
              android:icon="@mipmap/ic_launcher"
              android:label="@string/app_name"
              android:supportsRtl="true"
              android:theme="@style/Theme.SwitchIcon">


              <!-- 原MainActivity -->
              <activity android:name=".MainActivity" />

              <!-- 固定設(shè)置一個默認的別名,用來替代原MainActivity -->
              <activity-alias
                  android:name=".DefaultAliasActivity"
                  android:enabled="true"
                  android:icon="@mipmap/ic_launcher"
                  android:label="@string/app_name"
                  android:targetActivity=".MainActivity">

                  <intent-filter>
                      <action android:name="android.intent.action.MAIN" />
                      <category android:name="android.intent.category.LAUNCHER" />
                  </intent-filter>
              </activity-alias>


              <!-- 別名1,特定活動需要的圖標如:雙11,國慶節(jié)等 -->
              <activity-alias
                  android:name=".Alias1Activity"
                  android:enabled="false"
                  android:icon="@mipmap/ic_launcher_show"
                  android:label="@string/app_name"
                  android:targetActivity=".MainActivity">

                  <intent-filter>
                      <action android:name="android.intent.action.MAIN" />

                      <category android:name="android.intent.category.LAUNCHER" />
                  </intent-filter>
              </activity-alias>

          </application>



          activity-alias標簽中的屬性如下:


          標簽作用
          android:name別名,命名規(guī)則同Actively
          android:enabled是否啟用別名,這里的主要作用的控制顯示應(yīng)用圖標
          android:icon應(yīng)用圖標
          android:label應(yīng)用名
          android:targetActivity必須指向原入口Activity

          在MainActivity中,通過啟用或禁用別名進行圖標切換


          /**
           * 設(shè)置默認的別名為啟動入口
           */

          public void setDefaultAlias() {
              PackageManager packageManager = getPackageManager();

              ComponentName name1 = new ComponentName(this"com.fengfeibiao.switchicon.DefaultAliasActivity");
              packageManager.setComponentEnabledSetting(name1, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

              ComponentName name2 = new ComponentName(this"com.fengfeibiao.switchicon.Alias1Activity");
              packageManager.setComponentEnabledSetting(name2, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
              }

          /**
           * 設(shè)置別名1為啟動入口
           */

          public void setAlias1() {
              PackageManager packageManager = getPackageManager();
              ComponentName name1 = new ComponentName(this"com.fengfeibiao.switchicon.DefaultAliasActivity");
              packageManager.setComponentEnabledSetting(name1, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);

              ComponentName name2 = new ComponentName(this"com.fengfeibiao.switchicon.Alias1Activity");
              packageManager.setComponentEnabledSetting(name2, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
          }



          ForegroundCallbacks監(jiān)聽App前后臺切換


          /**
           * 監(jiān)聽App前后臺切換
           */

          public class ForegroundCallbacks implements Application.ActivityLifecycleCallbacks {
              public static final long CHECK_DELAY = 500;
              public static final String TAG = ForegroundCallbacks.class.getName();

              public interface Listener {
                  void onForeground();

                  void onBackground();
              }

              private static ForegroundCallbacks instance;
              private boolean foreground = false, paused = true;
              private Handler handler = new Handler();
              private List<Listener> listeners = new CopyOnWriteArrayList<Listener>();
              private Runnable check;

              public static ForegroundCallbacks init(Application application) {
                  if (instance == null) {
                      instance = new ForegroundCallbacks();
                      application.registerActivityLifecycleCallbacks(instance);
                  }
                  return instance;
              }

              public static ForegroundCallbacks get(Application application) {
                  if (instance == null) {
                      init(application);
                  }
                  return instance;
              }

              public static ForegroundCallbacks get(Context ctx) {
                  if (instance == null) {
                      Context appCtx = ctx.getApplicationContext();
                      if (appCtx instanceof Application) {
                          init((Application) appCtx);
                      }
                      throw new IllegalStateException(
                              "Foreground is not initialised and " +
                                      "cannot obtain the Application object");
                  }
                  return instance;
              }

              public static ForegroundCallbacks get() {
                  if (instance == null) {
                      throw new IllegalStateException(
                              "Foreground is not initialised - invoke " +
                                      "at least once with parameterised init/get");
                  }
                  return instance;
              }

              public boolean isForeground() {
                  return foreground;
              }

              public boolean isBackground() {
                  return !foreground;
              }

              public void addListener(Listener listener) {
                  listeners.add(listener);
              }

              public void removeListener(Listener listener) {
                  listeners.remove(listener);
              }

              @Override
              public void onActivityResumed(Activity activity) {
                  paused = false;
                  boolean wasBackground = !foreground;
                  foreground = true;
                  if (check != null)
                      handler.removeCallbacks(check);
                  if (wasBackground) {
                      Log.d(TAG, "went foreground");

                      for (Listener l : listeners) {
                          try {
                              l.onForeground();


                          } catch (Exception exc) {
                              Log.d(TAG, "Listener threw exception!:" + exc.toString());
                          }
                      }
                  } else {
                      Log.d(TAG, "still foreground");
                  }
              }

              @Override
              public void onActivityPaused(Activity activity) {
                  paused = true;
                  if (check != null)
                      handler.removeCallbacks(check);
                  handler.postDelayed(check = new Runnable() {
                      @Override
                      public void run() {
                          if (foreground && paused) {
                              foreground = false;
                              Log.d(TAG, "went background");
                              for (Listener l : listeners) {
                                  try {
                                      l.onBackground();
                                  } catch (Exception exc) {
                                      Log.d(TAG, "Listener threw exception!:" + exc.toString());
                                  }
                              }
                          } else {
                              Log.d(TAG, "still foreground");
                          }
                      }
                  }, CHECK_DELAY);
              }

              @Override
              public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
              }

              @Override
              public void onActivityStarted(Activity activity) {
              }

              @Override
              public void onActivityStopped(Activity activity) {
              }

              @Override
              public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
              }

              @Override
              public void onActivityDestroyed(Activity activity) {
              }
          }



          需要在Application中調(diào)用ForegroundCallbacks.init(this)進行初始化。


          在MainActivity中實現(xiàn)ForegroundCallbacks.Listener對App進行監(jiān)聽,當處于后臺判斷是否切換應(yīng)用圖標


          完整的MainActivity代碼:



          public class MainActivity extends AppCompatActivity implements ForegroundCallbacks.Listener {

              private int position = 0;

              @Override
              protected void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  setContentView(R.layout.activity_main);
                  //添加app前后臺監(jiān)聽
                  ForegroundCallbacks.get(this).addListener(this);

                  findViewById(R.id.tv_default).setOnClickListener(new View.OnClickListener() {
                      @Override
                      public void onClick(View v) {
                          position = 0;
                      }
                  });

                  findViewById(R.id.tv_alias1).setOnClickListener(new View.OnClickListener() {
                      @Override
                      public void onClick(View v) {
                          position = 1;
                      }
                  });

              }

              @Override
              protected void onDestroy() {
                  // 移除app前后臺監(jiān)聽
                  ForegroundCallbacks.get(this).removeListener(this);
                  super.onDestroy();
              }

              @Override
              public void onForeground() {

              }

              @Override
              public void onBackground() {
                  //根據(jù)具體業(yè)務(wù)需求設(shè)置切換條件,我公司采用接口控制icon切換
                  if (position == 0) {
                      setDefaultAlias();
                  } else {
                      setAlias1();
                  }
              }


              /**
               * 設(shè)置默認的別名為啟動入口
               */

              public void setDefaultAlias() {
                  PackageManager packageManager = getPackageManager();

                  ComponentName name1 = new ComponentName(this"com.fengfeibiao.switchicon.DefaultAliasActivity");
                  packageManager.setComponentEnabledSetting(name1, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

                  ComponentName name2 = new ComponentName(this"com.fengfeibiao.switchicon.Alias1Activity");
                  packageManager.setComponentEnabledSetting(name2, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
              }

              /**
               * 設(shè)置別名1為啟動入口
               */

              public void setAlias1() {
                  PackageManager packageManager = getPackageManager();

                  ComponentName name1 = new ComponentName(this"com.fengfeibiao.switchicon.DefaultAliasActivity");
                  packageManager.setComponentEnabledSetting(name1, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);

                  ComponentName name2 = new ComponentName(this"com.fengfeibiao.switchicon.Alias1Activity");
                  packageManager.setComponentEnabledSetting(name2, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
              }
          }

          5.具體缺陷


          具體缺陷如下:


          1. 切換icon會關(guān)閉應(yīng)用進程,不是崩潰所以不會上報bugly。


          2. 切換icon需要時間,部分華為機型要10s左右,之后能正常打開。


          3. 切換icon過程中,部分機型點擊圖標無法打開應(yīng)用,提示應(yīng)用未安裝。


          2021.11.15更新 魅族機型 16S 不能動態(tài)切換icon


          我公司已采用本方案投入生產(chǎn),后續(xù)發(fā)現(xiàn)有缺陷及解決辦法會及時更新。如采用本方案有其他問題,歡迎留言。


          Demo的github地址

          https://github.com/FengFeiBiao/SwitchIcon


          參考文章


          【監(jiān)聽App進入前后臺】

          https://blog.csdn.net/mapboo/article/details/104073789






          為了防止失聯(lián),歡迎關(guān)注我防備的小號



           

                                                   微信改了推送機制,真愛請星標本公號??

          瀏覽 145
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  殴美肏屄视频免费看 | 免费性爱视频 | 女人色毛片女人色毛片18 | 成人免费大片黄在线播放 | 午夜影院中文字幕 |