背景
最近在準備面試麻蹋,結合之前的工作經驗和近期在網上收集的一些面試資料皂贩,準備將Android開發(fā)崗位的知識點做一個系統(tǒng)的梳理菲驴,整理成一個系列:Android應用開發(fā)崗 面試匯總会傲。本系列將分為以下幾個大模塊:
Java基礎篇混卵、Java進階篇题翰、常見設計模式
Android基礎篇恶阴、Android進階篇诈胜、性能優(yōu)化
網絡相關、數(shù)據(jù)結構與算法
常用開源庫冯事、Kotlin焦匈、Jetpack
注1:以上文章將陸續(xù)更新,直到我找到滿意的工作為止昵仅,有跳轉鏈接的表示已發(fā)表的文章缓熟。
注2:該系列屬于個人的總結和網上東拼西湊的結果,每個知識點的內容并不一定完整摔笤,有不正確的地方歡迎批評指正够滑。
注3:部分摘抄較多的段落或有注明出處。如有侵權吕世,請聯(lián)系本人進行刪除彰触。
Fragment相關
Activity與Fragment之間生命周期比較
Activity于Fragment的生命周期的不同主要表現(xiàn)在onCreate和onDestroy兩個生命周期方法中:
- 在Activity的onCreate方法回調時,在回調期間命辖,F(xiàn)ragment首先會執(zhí)行
onAttach()-->onCreate()-->onCreateView()-->onActivityCreated() - 在Activity的onDestroy()回調時况毅,在回調期間,F(xiàn)ragment首先會執(zhí)行onDestroyView()-->onDestroy()-->onDetach()
Fragment 如何實現(xiàn)類似 Activity 棧的壓棧和出棧效果尔艇?
Fragment 的事物管理器(FragmentManager )內部維持了一個雙向鏈表結構尔许,該結構可以記錄我們每次 add 的Fragment 和 replace 的 Fragment,然后當我們點擊 back 按鈕的時候會自動幫我們實現(xiàn)退棧操作终娃。
Fragment與Activity之間是如何傳值的味廊?
- Activity向Fragment傳值:將要傳的值,放到bundle對象里尝抖,在Activity中創(chuàng)建該Fragment的對象fragment毡们,
通過調用 fragment.setArguments()傳遞到fragment中;在該Fragment中通過調用getArguments()得到bundle對象昧辽,就能得到里面的值衙熔。 - Fragment向Activity傳值:在Activity中拿到Fragment的實例,通過回調的方式搅荞,在Activity中實現(xiàn)Fragment的回調红氯。
- 通過在Activity中生成ViewModel,在Fragment使用該ViewModel
Fragment懶加載
為什么要實現(xiàn)懶加載咕痛?
在沒有添加懶加載之前痢甘,只要使用 add+show+hide 的方式控制并顯示 Fragment, 那么不管 Fragment 是否嵌套,在初始化后茉贡,如果只調用了add+show塞栅,同級下的 Fragment 的相關生命周期函數(shù)都會被調用。且調用的生命周期函數(shù)如下所示:(不管該Fragment是否用戶可見腔丧,都會調用以下函數(shù)放椰,常見在viewpager中)
onAttach -> onCreate -> onCreatedView -> onActivityCreated -> onStart -> onResume
因此作烟,為了避免性能和流量的浪費,需要對當前不可見的Fragment進行懶加載處理砾医,即Fragment真正被用戶看到時才開始去加載數(shù)據(jù)拿撩。
add+show+hide 模式下的老方案
場景:通過show+hide方法控制兩個fragment的顯示,A和B如蚜,當從A切換到B時压恒,兩個Fragment的可見狀態(tài)發(fā)生了改變,F(xiàn)ragment會回調onHiddenChanged()方法错邦,A中isHidden()方法的值為 true探赫,B中為false。
方案:
- 只要通過 show+hide 方式控制 Fragment 的顯隱兴猩,那么在第一次初始化后期吓,F(xiàn)ragment 任何的生命周期方法都不會調用早歇,只有 onHiddenChanged 方法會被調用倾芝。
- 另,假如我們要在 add+show+hide 模式下控制 Fragment 的懶加載箭跳,我們只需要做這兩步:
因此
- 1晨另、我們需要在 onResume() 函數(shù)中調用 isHidden() 函數(shù),來處理默認顯示的 Fragment谱姓;
- 2借尿、在 onHiddenChanged 函數(shù)中控制其他不可見的Fragment,即:
abstract class LazyFragment:Fragment(){
private var isLoaded = false //控制是否執(zhí)行懶加載
override fun onResume() {
super.onResume()
judgeLazyInit()
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
judgeLazyInit()
}
private fun judgeLazyInit() {
if (!isLoaded && !isHidden()) {
lazyInit()
isLoaded = true
}
}
override fun onDestroyView() {
super.onDestroyView()
isLoaded = false
}
//懶加載方法
abstract fun lazyInit()
}
ViewPager+Fragment 模式下的老方案
ViewPager+Fragment 模式下的老方案
使用傳統(tǒng)方式處理 ViewPager 中 Fragment 的懶加載屉来,我們需要控制 setUserVisibleHint(boolean isVisibleToUser) 函數(shù)路翻,該函數(shù)與之前我們介紹的 onHiddenChanged() 作用非常相似,都是通過傳入的參數(shù)值來判斷當前 Fragment 是否對用戶可見茄靠,只是 onHiddenChanged() 是在 add+show+hide 模式下使用茂契,而 setUserVisibleHint 是在 ViewPager+Fragment 模式下使用。public void setUserVisibleHint(boolean isVisibleToUser) {}
因此:
abstract class LazyFragment : Fragment() {
//是否執(zhí)行懶加載
private var isLoaded = false
//當前Fragment是否對用戶可見
private var isVisibleToUser = false
/**
* 當使用ViewPager+Fragment形式會調用該方法時慨绳,setUserVisibleHint會優(yōu)先Fragment生命周期函數(shù)調用掉冶,
* 所以這個時候就,會導致在setUserVisibleHint方法執(zhí)行時就執(zhí)行了懶加載,
* 而不是在onResume方法實際調用的時候執(zhí)行懶加載脐雪。所以需要這個變量
*/
private var isCallResume = false
override fun onResume() {
super.onResume()
isCallResume = true
judgeLazyInit()
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
isVisibleToUser = !hidden
judgeLazyInit()
}
private fun judgeLazyInit() {
if (!isLoaded && isVisibleToUser && isCallResume) {
lazyInit()
Log.d(TAG, "lazyInit:!!!!!!!")
isLoaded = true
}
}
//在Fragment銷毀View的時候厌小,重置狀態(tài)
override fun onDestroyView() {
super.onDestroyView()
isLoaded = false
isVisibleToUser = false
isCallResume = false
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
this.isVisibleToUser = isVisibleToUser
judgeLazyInit()
}
abstract fun lazyInit()
}
Androidx 下的懶加載
雖然之前的方案就能解決輕松的解決 Fragment 的懶加載,但這套方案有一個最大的弊端战秋,就是不可見的 Fragment 執(zhí)行了 onResume() 方法璧亚。onResume 方法設計的初衷,難道不是當前 Fragment 可以和用戶進行交互嗎脂信?你他媽既不可見癣蟋,又不能和用戶進行交互拐袜,你執(zhí)行 onResume 方法干嘛?
基于此問題梢薪,Google 在 Androidx 在 FragmentTransaction 中增加了 setMaxLifecycle 方法來控制 Fragment 所能調用的最大的生命周期函數(shù)蹬铺。即Fragment被用戶可見和可操作時才調用onResume方法:
add+show+hide 模式下的新方案
通過FragmentTransaction 設置setMaxLifecycle的值:setMaxLifecycle(Lifecycle.State.STARTED)
,可以使Fragment生命周期降級到onStart。
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
homefragment = new HomeFragment();
ft.add(R.id.fl_container, homefragment);
ft.setMaxLifecycle(homefragment, Lifecycle.State.STARTED);
ft.commit();
ViewPager+Fragment 模式下的老方案
該場景下秉撇,通過調用FragmentStatePagerAdapter中的setPrimaryItem(FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)
注:該方法最終也是調setMaxLifecycle()甜攀。
以上兩種方式只需要在onResume中增加是否已經加載過數(shù)據(jù)的判斷,來決定是否需要執(zhí)行初次加載琐馆。
參考鏈接
Service相關
簡介:
Service(服務)是一個可以在后臺執(zhí)行長時間運行操作而沒有用戶界面的應用組件规阀。Service是運行在主線程中的(跟Activity同一個線程),不能進行耗時操作瘦麸。我們一般都是在Service中創(chuàng)建一個新的線程來處理一些耗時工作谁撼,這樣就不會阻塞主線程。
啟動Service的兩種方式
- startService方式啟動
如果運行在后臺的Service甚至不需要和UI(主)線程間進行交互滋饲,這種情況下厉碟,一般是調用startService來啟動Service。通過 startService啟動屠缭,Service 會經歷 onCreate 到 onStartCommand箍鼓,然后處于運行狀態(tài),stopService的時候調用 onDestroy方法呵曹。 如果是調用者自己直接退出而沒有調用 stopService 的話款咖,Service 會一直在后臺運行。 - bindService方式啟動
兩個不同進程間通信 或者 某個應用中Service方法的暴露出去(同個進程間)奄喂,一般是調用bindService來啟動Service铐殃。通過 bindService啟動,Service 會運行 onCreate跨新,然后是調用 onBind富腊, 這個時候調用者和 Service綁定在一起。調用者退出了玻蝌,Srevice 就會調用 onUnbind->onDestroyed 方法蟹肘。
所謂綁定在一起就共存亡了,調用者也可以通過調用 unbindService 方法來停止服務。
Service的生命周期
回調方法說明
- onCreate:如果多次執(zhí)行了Context的startService方法啟動Service俯树,Service方法的onCreate方法只會在第一次創(chuàng)建Service的時候調用一次帘腹,以后均不會再次調用。我們可以在onCreate方法中完成一些Service初始化相關的操作
- onStartCommand:如果多次執(zhí)行了Context的startService方法许饿,那么Service的onStartCommand方法也會相應的多次調用阳欲。onStartCommand方法很重要,我們在該方法中根據(jù)傳入的Intent參數(shù)進行實際的操作,比如會在此處創(chuàng)建一個子線程用于下載數(shù)據(jù)或播放音樂等
- onBind:Service中的onBind方法是個抽象方法球化,所以Service類本身就是一個抽象類秽晚,也就是說onBind方法必須要重寫,即使用不到筒愚。通過startService使用Service時赴蝇,我們在重寫onBind方法時,只需要將其返回值設為null即可巢掺。onBind方法主要是用于給bindService方法調用Service時才使用到句伶。
- onDestroy:Service銷毀時回調函數(shù)
常見面試題
1、什么是 IntentService陆淀?有何優(yōu)點考余?
- IntentService 是 Service 的子類,比普通的 Service 增加了額外的功能轧苫。
先看 Service 本身存在兩個問題:
- Service 不會專門啟動一條單獨的進程楚堤,Service 與它所在應用位于同一個進程中;
- Service 也不是專門一條新線程含懊,因此不應該在 Service 中直接處理耗時的任務身冬;
- IntentService 特征
1、會創(chuàng)建獨立的 worker 線程來處理所有的 Intent 請求绢要;
2吏恭、會創(chuàng)建獨立的 worker 線程來處理 onHandleIntent()方法實現(xiàn)的代碼拗小,無需處理多線程問題重罪;
3、所有請求處理完成后哀九,IntentService 會自動停止剿配,無需調用 stopSelf()方法停止 Service;
4阅束、為Service 的 onBind()提供默認實現(xiàn)呼胚,返回 null;
5息裸、為Service 的 onStartCommand 提供默認實現(xiàn)蝇更,將請求 Intent 添加到隊列中;
2呼盆、怎么保證應用盡可能不被殺死
- 通過保證Service不被銷毀的方式(提高Service所在進程的優(yōu)先級)
- 手機廠商將應用加入白名單
保證Service不被銷毀
- 1年扩、在onStartCommand方法中將flag設置為START_STICKY,即return Service.START_STICKY;
- 2访圃、在xml中設置了android:priority
//設置服務的優(yōu)先級為MAX_VALUE
<service android:name=".MyService"
android:priority="2147483647" >
</service>
- 3厨幻、在onStartCommand方法中設置為前臺進程
通過設置一個狀態(tài)欄顯示來實現(xiàn)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Notification notification = new Notification(R.mipmap.ic_launcher, "服務正在運行",System.currentTimeMillis());
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,notificationIntent,0);
RemoteViews remoteView = new RemoteViews(this.getPackageName(),R.layout.notification);
remoteView.setImageViewResource(R.id.image, R.mipmap.ic_launcher);
remoteView.setTextViewText(R.id.text , "Hello,this message is in a custom expanded view");
notification.contentView = remoteView;
notification.contentIntent = pendingIntent;
startForeground(1, notification);
return Service.START_STICKY;
}
- 4、在onDestroy方法中重啟service
@Override
public void onDestroy() {
super.onDestroy();
startService(new Intent(this, MyService.class));
}
- 5、用AlarmManager.setRepeating(…)方法循環(huán)發(fā)送鬧鐘廣播,接收的時候調用service的onstart方法
Intent intent = new Intent(MainActivity.this,MyAlarmReciver.class);
PendingIntent sender = PendingIntent.getBroadcast( MainActivity.this, 0, intent, 0);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND, 1);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
//重復鬧鐘
/**
* @param triggerAtMillis t 鬧鐘的第一次執(zhí)行時間况脆,以毫秒為單位
* @param intervalMillis 表示兩次鬧鐘執(zhí)行的間隔時間饭宾,也是以毫秒為單位
* @param operation 綁定了鬧鐘的執(zhí)行動作,比如發(fā)送一個廣播格了、給出提示等等
*/
am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 2 * 1000, sender);
Broadcast Receiver相關
如何注冊 BroadcastReceiver
在清單文件中注冊廣播接收者稱為靜態(tài)注冊看铆,在代碼中注冊稱為動態(tài)注冊。
- 靜態(tài)注冊的廣播接收者只要 app 在系統(tǒng)中運行則一直可以接收到廣播消息盛末,
- 動態(tài)注冊的廣播接收者當注冊的 Activity 或者 Service 銷毀了那么就接收不到廣播了性湿。
靜態(tài)注冊:在清單文件中進行如下配置
<receiver android:name=".BroadcastReceiver1" >
<intent-filter>
<action android:name="android.intent.action.CALL" > </action>
</intent-filter>
</receiver>
動態(tài)注冊:在代碼中進行如下注冊
receiver = new BroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(CALL_ACTION);
context.registerReceiver(receiver, intentFilter);
Android 引入廣播機制的用意?
- 程序間互通消息(例如在自己的應用程序內監(jiān)聽系統(tǒng)來電)
- 效率上(參考 UDP 的廣播協(xié)議在局域網的方便性)
- 設計模式上(反轉控制的一種應用,類似觀察者模式)
兩種注冊各有什么特點
靜態(tài)注冊
- 常駐满败,當應用程序關閉后如果有信息廣播來肤频,程序也會被系統(tǒng)調用,自己運行算墨。
- 無需擔憂廣播接收器是否被關閉宵荒,只要設備是開啟狀態(tài),廣播接收器就是打開著的净嘀。
動態(tài)注冊
- 不常駐报咳,廣播會跟隨程序的生命周期。
- 在 Android 的廣播機制中挖藏,動態(tài)注冊優(yōu)先級高于靜態(tài)注冊優(yōu)先級暑刃,因此在必要情況下,是需要動態(tài)注冊廣播接收者的膜眠。
當用來注冊的 Activity 關掉后岩臣,廣播也就失效了。
BrocastReceiver 的生命周期和注意事項
- BroadCastReceiver 的生命周期很短暫宵膨,當接收到廣播的時候創(chuàng)建架谎,當onReceive()方法結束后銷毀
- 因為BroadCastReceiver的聲明周期很短暫,所以不要在廣播接收器中去創(chuàng)建子線程做耗時的操作辟躏,因為廣播接受者被銷毀后谷扣,這個子進程就會成為空進程,很容易被殺死
- BroadCastReceiver是運行在主線程的捎琐,所以不能直接在BroadCastReceiver中去做耗時的操作会涎,否則就會出現(xiàn)ANR異常
參考鏈接
動畫相關
動畫有哪幾類,各有什么特點瑞凑?
三類:補間動畫末秃、幀動畫、屬性動畫拨黔。補間動畫和Drawable動畫可統(tǒng)稱為視圖動畫蛔溃。也可以將這三類動畫都歸為屬性動畫绰沥。
補間動畫(View Animation)
在xml中定義,有縮放贺待、平移徽曲、漸變、旋轉等麸塞,可組合使用秃臣。動畫集合 AnimationSet用來存放上面四種動畫的集合
幀動畫
也叫Frame動畫,可以劃分到視圖動畫的類別哪工。專門用來一個一個的顯示Drawable的resources奥此,就像放幻燈片一樣
屬性動畫
通過改變視圖的屬性,來達到動畫的效果的動畫雁比。
- 支持對所有View能更新的屬性的動畫(需要屬性的setXxx()和getXxx())稚虎。
- 更改的是View實際的屬性,所以不會影響其在動畫執(zhí)行后所在位置的正常使用偎捎。
參考鏈接
過渡動畫和MotionLayout
- MotionLayout是ConstraintLayout的子類蠢终,所以它是一種布局類型,但是它能夠為布局屬性添加動畫效果茴她,是開發(fā)者實現(xiàn)動畫效果的另一個新的選擇寻拂。
- 它可以直接通過觸摸屏幕來控制動畫的運行進度。也就是說MotionLayout會管理你的觸摸事件通過跟蹤手指的速度丈牢,并將其與系統(tǒng)中的視圖速度相匹配祭钉。從而可以自然地在兩者之間通過觸摸滑動平穩(wěn)過渡。并且在動畫里面加入了關鍵幀的概念己沛,使得其自動生成動畫在運行時某一階段會運行到關鍵幀的狀態(tài)慌核。
- 它具有ConstraintLayout的所有屬性。MotionLayout用來處理兩個ConstraintSet之間的切換泛粹,并在根據(jù)兩個ConstraintSet的CustomAttribute參數(shù)來自動生成切換動畫遂铡。
- MotionLayout支持在XML中完全描述一個復雜的動畫,而不需要通過Java代碼來實現(xiàn)晶姊。
Window和WindowManager
類結構圖:
SurfaceView相關
我們知道View是通過刷新來重繪視圖,系統(tǒng)通過發(fā)出VSSYNC信號來進行屏幕的重繪伪货,刷新的時間間隔是16ms,如果我們可以在16ms以內將繪制工作完成们衙,則沒有任何問題,如果我們繪制過程邏輯很復雜碱呼,并且我們的界面更新還非常頻繁蒙挑,這時候就會造成界面的卡頓,影響用戶體驗愚臀,為此Android提供了SurfaceView來解決這一問題忆蚀。如:繪制手寫簽名
View和SurfaceView的區(qū)別:
- 1 . View適用于主動更新的情況,而SurfaceView則適用于被動更新的情況,比如頻繁刷新界面馋袜。
- 2 . View在主線程中對頁面進行刷新男旗,而SurfaceView則開啟一個子線程來對頁面進行刷新。
- 3 . View在繪圖時沒有實現(xiàn)雙緩沖機制欣鳖,SurfaceView在底層機制中就實現(xiàn)了雙緩沖機制察皇。
Android中的重要術語解釋
- 1.ActivityManagerServices,簡稱AMS泽台,服務端對象什荣,負責系統(tǒng)中所有Activity的生命周期
- 2.ActivityThread,App的真正入口怀酷。當開啟App之后稻爬,會調用main()開始運行,開啟消息循環(huán)隊列蜕依,這就是傳說中的UI線程或者叫主線程因篇。與ActivityManagerServices配合,一起完成Activity的管理工作
- 3.ApplicationThread笔横,用來實現(xiàn)ActivityManagerService與ActivityThread之間的交互竞滓。在ActivityManagerService需要管理相關Application中的Activity的生命周期時,通過ApplicationThread的代理對象與ActivityThread通訊吹缔。
- 4.ApplicationThreadProxy商佑,是ApplicationThread在服務器端的代理,負責和客戶端的ApplicationThread通訊厢塘。AMS就是通過該代理與ActivityThread進行通信的茶没。
- 5.Instrumentation,每一個應用程序只有一個Instrumentation對象晚碾,每個Activity內都有一個對該對象的引用抓半。Instrumentation可以理解為應用進程的管家,ActivityThread要創(chuàng)建或暫停某個Activity時格嘁,都需要通過Instrumentation來進行具體的操作笛求。
- 6.ActivityStack,Activity在AMS的棧管理糕簿,用來記錄已經啟動的Activity的先后關系探入,狀態(tài)信息等。通過ActivityStack決定是否需要啟動新的進程懂诗。
- 7.ActivityRecord梳侨,ActivityStack的管理對象馍佑,每個Activity在AMS對應一個ActivityRecord,來記錄Activity的狀態(tài)以及其他的管理信息艇潭。其實就是服務器端的Activity對象的映像。
- 8.TaskRecord,AMS抽象出來的一個“任務”的概念,是記錄ActivityRecord的棧,一個“Task”包含若干個ActivityRecord问窃。AMS用TaskRecord確保Activity啟動和退出的順序。如果你清楚Activity的4種launchMode胖喳,那么對這個概念應該不陌生泡躯。
其他面試題
invalidate、postInvalidate和requestLayout區(qū)別
- invalidate在主線程中調用丽焊,postInvalidate在子線程中調用较剃,通過ViewRootImpl中的ViewRootHandler進行線程調度,切到主線程技健,最終調的也是view.invalidate()
- invalidate() -> parent.invalidateChild() -> 層層找到parent写穴,parent.invalidateChildInParent(),直到ViewRootImpl
- ViewRootImpl.invalidateChildInParent() -> invalidateRectOnScreen() -> scheduleTraversals() -> doTraversal() -> performTraversals() -> 根據(jù)條件去執(zhí)行performMeasure() / performLayout / performDraw
- 也就是說任意一個子View調用invalidate雌贱,最終都會到ViewRootImpl的performTraversals
- invalidate是在view需要重繪的時候調用啊送,requestLayout方法調用后只會執(zhí)行當前view的measure和layout,而draw不一定被執(zhí)行欣孤,只有當view的位置發(fā)生改變才會執(zhí)行draw方法
Intent 可以傳遞哪些數(shù)據(jù)類型
- Serializable:Java提供的接口馋没,用來對數(shù)據(jù)進行序列化
- Charsequence:Java提供的接口,實現(xiàn)了這個接口的類有:CharBuffer/String/StringBuffer/StringBuilder這個四個類
- Parcelable:Android提供了一種新的類型:Parcel降传。本類被用作封裝數(shù)據(jù)的容器篷朵,封裝后的數(shù)據(jù)可以通過Intent或IPC傳遞。 除了基本類型以外婆排,只有實現(xiàn)了Parcelable接口的類才能被放入Parcel中声旺。
- Bundle
- 基礎數(shù)據(jù)類型和String/StringBuffer/StringBuilder
Bitmap和Drawable的區(qū)別是什么?
- 可以簡單地理解為 Bitmap 儲存的是像素信息段只,Drawable 儲存的是 對 Canvas 的一系列操作腮猖。而 BitmapDrawable 儲存的是「把 Bitmap 渲染到 Canvas 上」這個操作。
- Bitmap:存儲圖像每個像素數(shù)據(jù)的容器赞枕,是final修飾的類型澈缺,不允許被繼承
- Drawable:一種圖像繪制工具,調用canvas進行繪制鹦赎,儲存的是對Canva的一系列操作
它們之間可以互轉嗎谍椅?理論上是不可以的,但是可以通過自身生成出對方的類型來達到互轉的效果古话。
Webview與Js交互?
Webview調用Js
需要在主線程中調用
- 基本格式:
webView.loadUrl("javascript:methodName(parameterValues)");
- 調用Js無參無返回值函數(shù):
String call = "javascript:readyToGo()";
webView.loadUrl(call);
- 調用Js有參無返回值函數(shù)
String call = "javascript:alertMessage(\"" + "content" + "\")";
webView.loadUrl(call);
- 調用Js有參有返回值函數(shù)
String call = "javascript:alertMessage(\"" + "content" + "\")";
webView.evaluateJavascript(call, new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
Log.d("findCar",s);
}
});
Js通過WebView調用Java代碼
從API19開始锁施,Android提供了@JavascriptInterface對象注解的方式來建立起Javascript對象和Android原生對象的綁定陪踩,提供給JavScript調用的函數(shù)必須帶有@JavascriptInterface杖们。
實際上,就是3個步驟:
1.Java被Js調用的方法上加@JavascriptInterface注解肩狂;
2.WebView注冊JavaScriptInterface摘完;
3.js調用,如window.android.show("JavaScript called~!");
Js調用Android Toast方法
- 1.編寫Java原生方法并用使用@JavascriptInterface注解
@JavascriptInterface
public boolean show(String s){
Toast.makeText(getApplication(), s, Toast.LENGTH_SHORT).show();
return true傻谁;
}
- 2.注冊JavaScriptInterface
webView.addJavascriptInterface(this, "android");
addJavascriptInterface的作用是把this所代表的類映射為JavaScript中的android對象孝治。
- 3.編寫JavaScript代碼
function toastClick(){
var str=window.android.show("JavaScript called~!");
console.log(str);
}
通過H5打開App的某個頁面?
在manifest文件中最開始啟動的activity中添加:
<activity android:name=".activitys.MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="host"
android:pathPrefix="/pathPrefix"
android:scheme="scheme" />
</intent-filter>
</activity>
//注意host,pathPrefix,scheme都是自己自定義的审磁,只要與h5頁面調用的一致即可,如下所示
如果要跳轉到指定的頁面谈飒,在MainActivity的onCreate()中添加:
Intent intent = getIntent();
Uri uri = intent.getData();
if (uri != null) {
String routeId = uri.getQueryParameter("pid");
Intent intent0 = new Intent(MainActivity.this, ZhidingActivity.class);
startActivity(intent0);
}
uri.getQueryParameter("pid");獲取h5頁面?zhèn)鬟f的參數(shù),如果沒有的話可以忽略
注意一點态蒂,微信上對于app的喚醒有攔截杭措,在瀏覽器中才可以起作用