當前位置:首頁 » 安卓系統 » labelforandroid

labelforandroid

發布時間: 2023-07-20 13:33:26

① android開發怎麼讓個推服務不被手機系統殺掉

服務是一個應用程序組件,可以在後台執行長時間運行的操作,不提供用戶界面。一個應用程序組件可以啟動一個服務,它將繼續在後台運行,即使用戶切換到另一個應用程序。此外,一個組件可以綁定到一個服務與它交互,甚至執行進程間通信(IPC)。例如,一個服務可能處理網路通信,播放音樂,執行文件I/O,或與一個內容提供者交互,都在後台執行。
一個服務本質上講有兩種形式:
Started 啟動的
started 形式的服務是指當一個應用組件(比如 activity )通過startService()方法開啟的服務。一旦開啟,該服務就可以 無限期 地在後台運行,哪怕開啟它的組件被銷毀掉。
通常,開啟的服務執行一個單獨的操作且並不向調用者返回一個結果。
比如,可能從網路進行下載或者上傳一個文件。當任務完成,服務就該自我停止。
Bound 綁定的
bound 形式的服務是指一個應用組件通過調用 bindService() 方法與服務綁定。一個綁定的服務提供一個客戶-服務端介面,以允許組件與服務交互,發送請求,獲得結果,甚至執行進程間通信。一個綁定的服務只和與其綁定的組件同時運行。多個組件可以同時綁定到一個服務,但當全部接觸綁定後,服務就被銷毀。
雖然分這兩類,但是一個服務可以同時使用這兩種方式——可以用 started 無限期的運行,同時允許綁定。只需要在服務中實現兩個回調方法: onStartCommand() 允許組件開啟服務, onBind() 允許綁定。
不論應用程序是怎麼起服務的, 任何 應用程序都可以用這個服務。同樣的,任何組件可以使用一個 Activity 通過傳遞 Intent 開啟服務。你也可以在配置文件設置服務為私有來防止其他應用訪問該服務。
注意: 一個服務在進程中的主線程運行——一個服務 不會 創建自己的線程,也 不會 在另外的進程運行(除非另外指定)。這意味著,如果服務需要做一些頻繁佔用CPU的工作或者會發生阻塞的操作,你需要在服務中另開線程執行任務。這可以降低產生ANR的風險,提高用戶體驗。
基礎
創建一個服務需要建立一個 Service 相關的子類,然後需要實現一些回調方法,好在不同的生命周期內做對應處理和綁定服務,比較重要的方法如下:
onStartCommand()
當其他組件,如 activity 請求服務啟動時,系統會調用這個方法。一旦這個方法執行,服務就開始並且無限期的執行。如果實現這個方法,當這個服務完成任務後,需要你來調用 stopSelf() 或者 stopService() 停掉服務。如果只想提供綁定,不需要自己實現這個方法。
onBind()
當有其他組件想通過 bindService() 方法綁定這個服務時系統就會調用此方法。在實現的方法裡面,必須添加一個供客戶端使用的介面通過返回一個 IBinder 來與服務通信,這個方法必須實現。當然不想允許綁定的話,返回 null 即可。
onCreate()
服務第一次建立的時候會調用這個方法,執行一次性設置程序,在上面2個方法執行前調用。如果服務已存在,則不執行該方法。
onDestroy()
服務不再使用則使用該方法。服務應該實現這個方法來清理諸如線程,注冊的監聽器等資源。這是最後調用的方法。
安卓系統只會在內存佔用很高,必須恢復系統資源供當前運行程序的情況下強制停掉一個運行中的服務。如果服務綁定在當前運行的程序中,就幾乎不會被殺掉,如果服務聲明了在前台運行(其實在後台,只是給系統一個錯的信息來提高優先順序),就幾乎不會被殺掉。另外,如果一個服務正在運行,且運行了很久,系統就會根據運行時間把其排在後台任務列表的後面,則這個服務很容易被殺掉。根據onStartCommand() 的返回值設置,服務被殺掉後仍可以在資源充足的條件下立即重啟。
是用一個服務好還是開一個線程好
一個服務就是一個可以忽略交互,在後台獨立運行的組件,如果你需要這樣就用服務
如果你需要在用戶與程序交互時在主線程外執行任務,那就開個線程吧。
比如想播放音樂,但只在程序運行時播放,你可能在 onCreate() 開一個線程,在 onStart() 中開啟它,在 onStop() 停止它。也可以考慮使用 AsyncTask 或者HandlerThread 取代一般的線程。
記住,如果使用一個服務,它還是默認在主線程中運行,如果會發生阻塞,還是要在服務中另開線程的。
在 manifest 文件聲明服務
要使用服務就必須在 manifest 文件聲明要用的所有服務,只用在<application> 標簽內添加子標簽 <service> 即可。
<manifest ...>
...
<application ...>
<service android:name=".ExampleService"
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:icon="drawable resource"
android:permission="string"
android:process="string" >
...
</service>
</application>
</manifest>

下面對 service 標簽屬性做說明
android:name
你所編寫的服務類的類名,可填寫完整名稱,包名+類名,如com.example.test.ServiceA ,也可以忽略包名,用 . 開頭,如 .ServiceA,因為在 manifest 文件開頭會定義包名,它會自己引用。
一旦你發布應用,你就不能改這個名字(除非設置 android:exported="false"),另外 name 沒有默認值,必須定義。
android:enabled
是否可以被系統實例化,默認為 true
因為父標簽 <application> 也有 enable 屬性,所以必須兩個都為默認值true 的情況下服務才會被激活,否則不會激活。
android:exported
其他應用能否訪問該服務,如果不能,則只有本應用或有相同用戶ID的應用能訪問。當然除了該屬性也可以在下面 permission 中限制其他應用訪問本服務。
這個默認值與服務是否包含意圖過濾器 intent filters 有關。如果一個也沒有則為 false
android:isolatedProcess
設置 true 意味著,服務會在一個特殊的進程下運行,這個進程與系統其他進程分開且沒有自己的許可權。與其通信的唯一途徑是通過服務的API(binding and starting)。
android:label
可以顯示給用戶的服務名稱。如果沒設置,就用 <application> 的 lable 。不管怎樣,這個值是所有服務的意圖過濾器的默認 lable 。定義盡量用對字元串資源的引用。
android:icon
類似 label ,是圖標,盡量用 drawable 資源的引用定義。
android:permission
是一個實體必須要運行或綁定一個服務的許可權。如果沒有許可權,startService() , bindService() 或 stopService() 方法將不執行,Intent 也不會傳遞到服務。
如果屬性未設置,會由 <application> 許可權設置情況應用到服務。如果兩者都未設置,服務就不受許可權保護。
android:process
服務運行所在的進程名。通常為默認為應用程序所在的進程,與包名同名。<application> 元素的屬性 process 可以設置不同的進程名,當然組件也可設置自己的進程覆蓋應用的設置。
如果名稱設置為冒號 : 開頭,一個對應用程序私有的新進程會在需要時和運行到這個進程時建立。如果名稱為小寫字母開頭,服務會在一個相同名字的全局進程運行,如果有許可權這樣的話。這允許不同應用程序的組件可以分享一個進程,減少了資源的使用。
創建「啟動的」服務
啟動的(started)服務由 startService(Intent) 方法啟動,在服務中的onStartCommand() 方法里獲得 Intent 信息。關閉則由服務自己的方法 stopSelf() 或者由啟動服務的地方調用 stopService(Intent) 方法來關閉。並不會因為啟動服務的應用程序銷毀而關閉。
示例,一個應用需要保存數據到遠程資料庫,這時啟動一個服務,通過創建啟動的服務給服務傳遞數據,由服務執行保存行為,行為結束再自我銷毀。因為服務跟啟動它的應用在一個進程的主線程中,所以對於耗時的操作要起一個新的線程去做。
//activity中
Intent intent = new Intent(MainActivity.this, ServiceA.class);
intent.putExtra("name", strName);
startService(intent);
//service中
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
// 獲取數據
String strName = intent.getStringExtra("name");
// ... 資料庫操作
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
耗時的操作
}
}).run();
return Service.START_STICKY;
}

寫服務有2種,繼承 service 或者 IntentService 。後者是前者的子類。前者包含上面介紹的各種方法,用於普通的服務。後者可以自己開一個工作線程一個接一個處理多個請求。
繼承IntentService
大多數服務不需要同時處理多個請求,繼承 IntentService 是最好的選擇
IntentService處理流程
創建默認的一個 worker 線程處理傳遞給 onStartCommand() 的所有 intent ,不佔據應用的主線程
創建一個工作隊列一次傳遞一個 intent 到你實現的 onHandleIntent() 方法,避免了多線程
在所以啟動請求被處理後自動關閉服務,不需要調用 stopSelf()
默認提供 onBind() 的實現,並返回 null
默認提供 onStartCommand() 的實現,實現發送 intent 到工作隊列再到你的onHandleIntent() 方法實現。
這些都加入到 IntentService 中了,你需要做的就是實現構造方法和onHandleIntent() ,如下:
public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
@Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}

如果需要重寫其他回調方法,如 onCreate() , onStartCommand() 等,一定要調用super() 方法,保證 IntentService 正確處理 worker 線程,只有 onHandleIntent()和 onBind() 不需要這樣。如:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}

繼承Service
繼承 Service 就可以實現對請求多線程的處理,前面介紹了 service 的生命周期,可以按照生命周期實現方法。就不放示例了。
onStartCommand() 的返回值
返回一個整型值,用來描述系統在殺掉服務後是否要繼續啟動服務,返回值有三種:
START_NOT_STICKY
系統不重新創建服務,除非有將要傳遞來的 intent 。這是最安全的選項,可以避免在不必要的時候運行服務。
START_STICKY
系統重新創建服務並且調用 onStartCommand() 方法,但並不會傳遞最後一次傳遞的 intent ,只是傳遞一個空的 intent 。除非存在將要傳遞來的 intent ,那麼就會傳遞這些 intent 。這個適合播放器一類的服務,不需要執行命令,只需要獨自運行,等待任務。
START_REDELIVER_INTENT
系統重新創建服務並且調用 onStartCommand() 方法,傳遞最後一次傳遞的intent 。其餘存在的需要傳遞的intent會按順序傳遞進來。這適合像下載一樣的服務,立即恢復,積極執行。
如果想從服務獲得結果,可以用廣播來處理
創建「綁定的」服務
用 bindService() 方法將應用組件綁定到服務,建立一個長時間保持的聯系。
如果需要在 activity 或其他組件和服務交互或者通過進程間通信給其他應用程序提供本應用的功能,就需要綁定的服務。
建立一個綁定的服務需要實現 onBind() 方法返回一個定義了與服務通信介面的IBinder 對象。其他應用程序組件可以調用 bindService() 方法獲取介面並且調用服務上的方法。
創建一個綁定的服務,第一件事就是定義一個說明客戶端與服務通信方式的介面。這個介面必須是 IBinder 的實現,並且必須要從 onBind() 方法返回。一旦客戶端接收到了 IBinder ,就可以通過這個介面進行交互。
多個客戶端可以綁定到一個服務,可以用 unbindService() 方法解除綁定,當沒有組件綁定在服務上,這個服務就會被銷毀。
//activity中
private ServiceConnection connB = new ServiceConnection() {

@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
Log.v(tag, "Service B disconnected");
}

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
Log.v(tag, "Service B connected");
MyBinderB binder = (MyBinderB) service;
ServiceB SB = binder.getService();
SB.showLog();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent a = new Intent(MainActivity.this, ServiceB.class);
bindService(a, connB, BIND_AUTO_CREATE);
}
}

//ServiceB
public class ServiceB extends Service {
public void showLog() {
Log.i(tag, "serviceB-->showLog()");
}

public class MyBinderB extends Binder {

public ServiceB getService() {
return ServiceB.this;
}
}

private MyBinderB myBinderB = new MyBinderB();

@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return myBinderB;
}
}

啟動前台服務
前台服務是被認為是用戶已知的正在運行的服務,當系統需要釋放內存時不會優先殺掉該進程。前台進程必須發一個 notification 在狀態欄中顯示,直到進程被殺死。因為前台服務會一直消耗一部分資源,但不像一般服務那樣會在需要的時候被殺掉,所以為了能節約資源,保護電池壽命,一定要在建前台服務的時候發notification ,提示用戶。當然,系統提供的方法就是必須有 notification 參數的,所以不要想著怎麼把 notification 隱藏掉。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification noti = new Notification.Builder(this)
.setContentTitle("Title")
.setContentText("Message")
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pendingIntent)
.build();
startForeground(12346, noti);
return Service.START_STICKY;
}

startForeground() 方法就是將服務設為前台服務。參數12346就是這個通知唯一的id,只要不為0即可。

② 如何用代碼更改android中的radio控制項設為選中

Radio 對象代表 HTML 表單中的單選按鈕。在 HTML 表單中 <input type="radio"> 每出現一次,一個 Radio 對象就會被創建。

單選按鈕是表示一組互斥選項按鈕中的一個。當一個按鈕被選中,之前選中的按鈕就變為非選中的。當單選按鈕被選中或不選中時,該按鈕就會觸發 onclick 事件句柄。您可通過遍歷表單的 elements[] 數組來訪問 Radio 對象,或者通過使用 document.getElementById()。

一、單選按鈕控制項語法

1 <input name="Fruit" type="radio" value="" />

使用html input標簽,name為自定義,type類型為「radio」的表單.

二、radio單選按鈕代碼舉例

1、html代碼片段:

12345678 <form action="" method="get">
您最喜歡水果?<br /><br />
<label><input name="Fruit" type="radio" value="" />蘋果 </label>
<label><input name="Fruit" type="radio" value="" />桃子 </label>
<label><input name="Fruit" type="radio" value="" />香蕉 </label>
<label><input name="Fruit" type="radio" value="" />梨 </label>
<label><input name="Fruit" type="radio" value="" />其它 </label>
</form>

2.舉例代碼片段二(默認選中設置舉例):

123 <input type="radio" name="identity" value="學生" checked="checked" />學生
<input type="radio" name="identity" value="教師" />教師
<input type="radio" name="identity" value="管理員" />管理員

在代碼舉例二種, checked="checked" 表示默認選中項設置。

3.代碼舉例三(js操作radio):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gbk">
<script>
<!--
//選中2返回的也是1,找到第一個ID為該值的DOM,彈出 1
function getVById(){alert(document.getElementById('test11').value);}
function getVByName(){
var tt = document.getElementsByName('test11');
for (var iIndex = 0; iIndex < tt.length ; iIndex++ )
{
if(tt[iIndex].checked)
{
alert(tt[iIndex].value);
break;
}
}
};
-->
</script>
<title>http://www.169it.com</title>
</head>
<body>
<input type="radio" id="test11" name="test11" value="1" />測試1
<input type="radio" id="test11" name="test11" value="2" />測試2
<input type="button" value="BTN_ByID" onclick="getVById()" />
<input type="button" value="BTN_ByName" onclick="getVByName()" />
</body>
<html>

③ android 怎麼批量插入聯系人,高效率的方法

參考下面方法:
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
ArrayList<VCardEntry> list = params[0];
Iterator<VCardEntry> it = null;
if (list != null) {
it = list.iterator();
}
Logger.v(TAG,"--->doInBackground it:"+it);
int rawContactInsertIndex = 0;
while(it!= null && it.hasNext()) {
VCardEntry mv = it.next();
rawContactInsertIndex = ops.size(); // 有了它才能給真正的實現批量添加
Logger.v(TAG,"--->>>>>>>name:"+mv.getDisplayName());
Logger.v(TAG,"--->>>>>>>getPhoneList:"+mv.getPhoneList());
if (mv.getPhoneList() != null) {
ops.add(ContentProviderOperation
.newInsert(RawContacts.CONTENT_URI)
.withValue(RawContacts.ACCOUNT_TYPE, ACCOUNT_NAME)
.withValue(RawContacts.ACCOUNT_NAME, ACCOUNT_TYPE)
.withYieldAllowed(true).build());
// add name
ops.add(ContentProviderOperation
.newInsert(Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
.withValue(StructuredName.DISPLAY_NAME, mv.getDisplayName())
.withYieldAllowed(true).build());
// add number
for(VCardEntry.PhoneData phone : mv.getPhoneList()) {
Logger.v(TAG,"--->>>>>>>number:"+phone.getNumber());
ops.add(ContentProviderOperation
.newInsert(Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
.withValue(Phone.NUMBER, phone.getNumber())
.withValue(Phone.TYPE, Phone.TYPE_MOBILE)
.withValue(Phone.LABEL, "")
.withYieldAllowed(true).build());
}
}
}
ContentProviderResult[] results = null;
if (ops != null) {
try {
results = mContext.getContentResolver()
.applyBatch(ContactsContract.AUTHORITY, ops);
} catch (RemoteException e) {
Logger.e(TAG,String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
Logger.e(TAG,String.format("%s: %s", e.toString(), e.getMessage()));
}
}

④ Android應用如何支持不同尺寸的屏幕

想要Android應用適應不同尺寸的屏幕,思路如下:
一:不同的layout
Android手機屏幕大小不一,有480x320, 640x360, 800x480,讓app適應不同屏幕,需要在res目錄下創建不同的layout文件夾,比如layout-640x360,layout-800x480,所有的layout文件在編譯之後都會寫入R.java里,而系統會根據屏幕的大小自己選擇合適的layout進行使用。
二:hdpi、mdpi、ldpi
在之前的版本中,只有一個drawable,而2.1版本中有drawable-mdpi、drawable-ldpi、drawable-hdpi三個,這三個主要是為了支持多解析度。
drawable- hdpi、drawable- mdpi、drawable-ldpi的區別:
(1)drawable-hdpi裡面存放高解析度的圖片,如WVGA (480x800),FWVGA (480x854)
(2)drawable-mdpi裡面存放中等解析度的圖片,如HVGA (320x480)
(3)drawable-ldpi裡面存放低解析度的圖片,如QVGA (240x320)
系統會根據機器的解析度來分別到這幾個文件夾裡面去找對應的圖片。
更正:應該是對應不同density 的圖片
在開發程序時為了兼容不同平台不同屏幕,建議各自文件夾根據需求均存放不同版本圖片。
屏幕方向:
橫屏豎屏自動切換:
可以在res目錄下建立layout-port-800x600和layout-land兩個目錄,裡面分別放置豎屏和橫屏兩種布局文件,這樣在手機屏幕方向變化的時候系統會自動調用相應的布局文件,避免一種布局文件無法滿足兩種屏幕顯示的問題。
不同解析度橫屏豎屏自動切換:
以800x600為例 :
可以在res目錄下建立layout-port-800x600和layout-land-800x600兩個目錄
不切換:
以下步驟是網上流傳的,不過我自己之前是通過圖形化界面實現這個配置,算是殊途同歸,有空我會把圖片貼上來。
還要說明一點:每個activity都有這個屬性screenOrientation,每個activity都需要設置,可以設置為豎屏(portrait),也可以設置為無重力感應(nosensor)。
要讓程序界面保持一個方向,不隨手機方向轉動而變化的處理辦法:
在AndroidManifest.xml裡面配置一下就可以了。加入這一行android:screenOrientation="landscape"。
例如(landscape是橫向,portrait是縱向):
Java代碼:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ray.linkit"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Main"
android:label="@string/app_name"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".GamePlay"
android:screenOrientation="portrait"></activity>
<activity android:name=".OptionView"
android:screenOrientation="portrait"></activity>
</application>
<uses-sdk android:minSdkVersion="3" />
</manifest>
另外,android中每次屏幕的切換動會重啟Activity,所以應該在Activity銷毀前保存當前活動的狀態,在Activity再次Create的時候載入配置,那樣,進行中的游戲就不會自動重啟了!
有的程序適合從豎屏切換到橫屏,或者反過來,這個時候怎麼辦呢?可以在配置Activity的地方進行如下的配置android:screenOrientation="portrait"。這樣就可以保證是豎屏總是豎屏了,或者landscape橫向。
而有的程序是適合橫豎屏切換的。如何處理呢?首先要在配置Activity的時候進行如下的配置:android:configChanges="keyboardHidden|orientation",另外需要重寫Activity的 onConfigurationChanged方法。實現方式如下,不需要做太多的內容:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
// land do nothing is ok
} else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
// port do nothing is ok
}
}
寫一個支持多分辨的程序,基於1.6開發的,建立了三個資源文件夾drawable-hdpi drawable-mdpi drawable-ldpi,裡面分別存放72*72 48*48 36*36的icon圖標文件。當我在G1(1.5的系統)上測試時,圖標應該自適應為48*48才對啊,但實際顯示的是36*36。怎麼才能讓其自適應 48*48的icon圖標呢
解決辦法 drawable-hdpi drawable-mdpi drawable-ldpi改成drawable-480X320 drawable-800X480的多分辨支持的文件夾

⑤ Android創建桌面快捷方式(兼容Android 8.0)

在Android O原生桌面上,按照傳統創建快捷方式的形式,是不會產生快捷方式的。

傳統方式如下:

從Android 7.1(API 25)開始,新增了ShortcutManager,可以對桌面久按應用圖標彈出的快捷方式進行管理。

但是,Android 7.1上直接往桌面上添加快捷方式依然是使用上面說到的這種舊方式,但是Android O上,Google應該是想通過比較統一的介面來管理桌面快捷方式了,所以摒棄了這種形式,轉而使用ShortcutManager進行管理。所以API 26上,ShortcutManager進行管理。所以API 26上,ShortcutManager新增了對Pinned Shortcuts(固定快捷方式)的管理。

官文:
Apps can pin an existing shortcut (either static or dynamic) or an entirely new shortcut to a supported launcher programatically using requestPinShortcut(ShortcutInfo, IntentSender). You pass two arguments into this method:

A ShortcutInfo object – If the shortcut already exists, this object should contain only the shortcut』s ID. Otherwise, the new ShortcutInfo object must contain an ID, an intent, and a short label for the new shortcut.
A PendingIntent object – This intent represents the callback that your app receives if the shortcut is successfully pinned to the device』s launcher.

Note: If the user doesn』t allow the shortcut to be pinned to the launcher, the pinning process fails, and the Intent object that is passed into this PendingIntent object isn』t executed.

Note: Due to background execution limits introced in Android O, it』s best to use a manifest-declared receiver to receive a callback.
Also, to prevent other apps from invoking the receiver, add the attribute assignment android:exported=」false」 to the receiver』s manifest entry.

Note: As you add logic in your app to make requests to pin shortcuts, keep in mind that not all launchers support pinning of shortcuts. To determine whether your app can complete this process on a particular device, check the return value of isRequestPinShortcutSupported(). Based on this return value, you might decide to hide the option in your app that allows users to pin a shortcut.

Note: See also the support library APIs isRequestPinShortcutSupported(Context) and requestPinShortcut(Context, ShortcutInfoCompat, IntentSender), which works on Android versions lower than O by falling back to the deprecated private intent com.android.launcher.action.INSTALL_SHORTCUT.

譯:
應用程序可以使用requestPinShortcut(ShortcutInfo,IntentSender)將現有的快捷方式(靜態或動態)或全新的快捷方式固定到支持的啟動器。你通過這個方法的兩個參數:

ShortcutInfo對象 - 如果快捷方式已存在,則該對象應僅包含快捷方式的ID。否則,新的ShortcutInfo對象必須包含新快捷方式的ID,意圖和短標簽。
PendingIntent對象 - 此意圖表示如果快捷方式成功固定到設備的啟動器,您的應用程序將收到回調。

注意:如果用戶不允許將快捷方式固定在啟動器上,則固定進程將失敗,並且未執行傳入此PendingIntent對象的Intent對象。

注意:由於Android O中引入的後台執行限制,最好使用清單聲明的接收器來接收回調。
另外,為了防止其他應用程序調用接收器,將屬性賦值android:exported =「false」添加到接收者的清單條目中。

注意:當您在應用程序中添加邏輯以引導快捷方式時,請記住,並非所有啟動器都支持固定快捷方式。 要確定您的應用程序是否可以在特定設備上完成此過程,請檢查isRequestPinShortcutSupported()的返回值。 根據此返回值,您可以決定隱藏您應用程序中允許用戶固定快捷方式的選項。

注意:另請參見支持庫API isRequestPinShortcutSupported(Context)和requestPinShortcut(Context,ShortcutInfoCompat,IntentSender),它可以在低於O的Android版本上運行,因為它們回落到不推薦使用的私有意圖com.android.launcher.action.INSTALL_SHORTCUT。

ShortcutManager類在API level 26上,增加了對isRequestPinShortcutSupported、requestPinShortcut、createShortcutResultIntent三個方法。說明如下:

1.isRequestPinShortcutSupported

官文:
Return TRUE if the app is running on a device whose default launcher supports requestPinShortcut(ShortcutInfo, IntentSender).

The return value may change in subsequent calls if the user changes the default launcher app.

Note: See also the support library counterpart isRequestPinShortcutSupported(Context), which supports Android versions lower than O using the legacy private intent com.android.launcher.action.INSTALL_SHORTCUT.

譯:
如果默認桌面支持requestPinShortcut(ShortcutInfo,IntentSender)方法,則返回TRUE。

如果用戶更改默認啟動程序應用程序,返回值可能會在後續調用中更改。

注意:另請參見支持庫對應的isRequestPinShortcutSupported(Context),在低於O的Android版本,它支持使用舊的私有意圖com.android.launcher.action.INSTALL_SHORTCUT。

2.requestPinShortcut

官文:
Request to create a pinned shortcut. The default launcher will receive this request and ask the user for approval. If the user approves it, the shortcut will be created, and resultIntent will be sent. If a request is denied by the user, however, no response will be sent to the caller.

Only apps with a foreground activity or a foreground service can call this method. Otherwise, it』ll throw IllegalStateException.

It』s up to the launcher to decide how to handle previous pending requests when the same package calls this API multiple times in a row. One possible strategy is to ignore any previous requests.

Note: See also the support library counterpart requestPinShortcut(Context, ShortcutInfoCompat, IntentSender), which supports Android versions lower than O using the legacy private intent com.android.launcher.action.INSTALL_SHORTCUT.

譯:
請求創建固定的快捷方式。默認啟動器將收到該請求,並要求用戶批准。如果用戶批准,將創建快捷方式,並且將發送resultIntent。但是,如果請求被用戶拒絕,則不會向呼叫者發送任何響應。

只有具有前台活動或前台服務的應用程序才能調用此方法。否則,它將拋出IllegalStateException。

當同一個軟體包連續多次調用該API時,由開發人員決定如何處理以前的待處理請求。一個可能的策略是忽略任何先前的請求。

注意:另請參見支持庫對應件requestPinShortcut(Context,ShortcutInfoCompat,IntentSender),在低於O的Android版本,它支持使用舊的私有意圖com.android.launcher.action.INSTALL_SHORTCUT。

3.createShortcutResultIntent

官文:
Returns an Intent which can be used by the default launcher to pin a shortcut containing the given ShortcutInfo. This method should be used by an Activity to set a result in response to ACTION_CREATE_SHORTCUT.

譯:
返回默認啟動器可以使用的Intent來固定包含給定的ShortcutInfo的快捷方式。 Activity應該使用此方法來設置響應ACTION_CREATE_SHORTCUT的結果。

根據彈窗提示可以看出,可以通過拖動這個圖標往桌面上添加快捷方式,可以通過點擊自動添加按鍵,系統給你在桌面的默認位置上添加。

添加後,桌面上會出現如圖所示的圖標:

回調用到的Receiver:

列印log發現,onReceive如圖官方文檔所說,點擊彈框自動添加按鍵後,會得到回調。但實踐發現,如果桌面上已經添加了圖標,當再次調用requestPinShortcut進行添加時,onReceive會在調用requestPinShortcut的時候,直接被回調,而且彈框也會彈出來。

在以上三個方法官方介紹中,官方提示我們,可以使用Android support庫的ShortcutManagerCompat進行快捷方式的版本適配。於是,在build.gradle中添加依賴進行嘗試:

⑥ 如何解決Android7.0及以上的許可權崩潰問題

話說自從google出來Android 7.0系統之後,我們公司的測試小夥伴就向我提出了7.0的bug,接下來就著手查閱了下問題是出在哪裡的,該如何來解決呢。
bug日誌是長這樣子的:
android.os.FileUriExposedException: file:///storage/emulated/0/ys_toutiao.apk exposed beyond app through Intent.getData()
at android.os.StrictMode.onFileUriExposed(StrictMode.java:1816)
at android.net.Uri.checkFileUriExposed(Uri.java:2350)
at android.content.Intent.prepareToLeaveProcess(Intent.java:9076)
at android.content.Intent.prepareToLeaveProcess(Intent.java:9037)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1530)
at android.app.Activity.startActivityForResult(Activity.java:4391)
at android.support.v4.app.BaseFragmentActivityJB.startActivityForResult(BaseFragmentActivityJB.java)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java)
at android.app.Activity.startActivityForResult(Activity.java:4335)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java)
at android.app.Activity.startActivity(Activity.java:4697)
at android.app.Activity.startActivity(Activity.java:4665)
at com.ijuyin.prints.news.utils.VersionUtils$1.handleMessage(VersionUtils.java)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6524)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:941)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:831)

也就是說是在什麼情況下會出現呢?
1、是在調用系統相機的時候;
2、是在寫入文件的時候, 在我這里是強制升級後下載完成安裝Apk時;
關於FileUriExposedException 異常的描述是這樣子的:
針對於許可權部分,Android7.0是google推出的對許可權做了一個更新即不允許出現以file://的形式調用隱式APP系統,也就是說以前呢,Uri的獲取方式是以file://xxx的樣式來,那麼我們也就是通過Uri.fromFile()來獲取如今放在7.0及以上系統呢,這樣子就不行啦;
如今的解決關鍵在哪裡呢,需要在應用間共享文件,也就是需要發送一項content://URI,並授予 URI 臨時訪問許可權。進行此授權的最簡單方式是使用FileProvider類。
嗯的,FileProvider
1、首先我們需要在AndroidManifest中的application下添加provider:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.ijuyin.prints.news">

<application
android:name="com.ijuyin.prints.news.base.BaseApplication"
android:allowBackup="true"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/text_app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:replace="android:label"
android:fullBackupContent="false">

<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.ijuyin.prints.news.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>

</application>

</manifest>

在這里我們需要注意一下其中設置的各種屬性的含義:
authorities:是該項目的包名+provider
grantUriPermissions:必須是true,表示授予 URI 臨時訪問許可權
exported:必須是false
resource:中的@xml/file_paths是我們接下來要在資源文件目錄下添加的文件
2、在res目錄下新建一個xml文件夾,並且新建一個file_paths的xml文件
設置file_path路徑.png
3、打開file_paths.xml文件添加如下內容
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<external-path
name="camera_photos"
path="."/>
<external-path
name="files_root"
path="Android/data/com.ijuyin.prints.news/"/>
<external-path
name="external_storage_root"
path="."/>
</paths>
</resources>

需要注意的是:
path:需要臨時授權訪問的路徑(.代表在相機調用時候訪問的是所有路徑,而文件寫入時訪問的路徑是Android/data/com.ijuyin.prints.news/)
name: 是你為設置的這個訪問路徑起的名字
4、接下來便是修改適配Android 7.0及以上系統的代碼
第一部分是對於相機模塊的修改
/**
* Open camera
*/
private void showCameraAction() {
if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission
.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE,
getString(R.string.mis_permission_rationale_write_storage),
REQUEST_STORAGE_WRITE_ACCESS_PERMISSION);
} else {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
try {
mTmpFile = FileUtils.createTmpFile(getActivity());
} catch (IOException e) {
e.printStackTrace();
}
Uri imageUri;
if (mTmpFile != null && mTmpFile.exists()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String authority = getActivity().getPackageName() + ".provider";
imageUri = FileProvider.getUriForFile(getActivity(), authority, mTmpFile);
} else {
imageUri = Uri.fromFile(mTmpFile);
}
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, REQUEST_CAMERA);
} else {
Toast.makeText(getActivity(), R.string.mis_error_image_not_exist, Toast
.LENGTH_SHORT).show();
}
} else {
Toast.makeText(getActivity(), R.string.mis_msg_no_camera, Toast.LENGTH_SHORT)
.show();
}
}
}

第二部分是對文件寫入模塊的代碼修改
private static Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == DOWN_UPDATE) {
mProgress.setProgress(progress);
} else if (msg.what == DOWN_OVER) {
if (null != downloadDialog && downloadDialog.isShowing()) {
try {
downloadDialog.dismiss();
} catch (Exception e) {
e.printStackTrace();
}
}
File apkfile = new File(apkPath);
if (!apkfile.exists()) {
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
String authority = mContext.getPackageName() + ".provider";
Uri contentUri = FileProvider.getUriForFile(mContext, authority, apkfile);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(apkfile), "application/vnd.android" +
".package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
mContext.startActivity(intent);
}
}
};

其中最核心的部分是

配置provider.png

需要注意的地方是:
首先我們對Android系統的型號做出判斷
添加flags,表明我們要被授予什麼樣的臨時許可權
以前我們直接 Uri.fromFile(apkFile)構建出一個Uri,現在我們使用FileProvider.getUriForFile(getActivity(), getActivity().getPackageName() + ".provider", mTmpFile);
其中getActivity().getPackageName()指的是該項目的應用包名(此處調用的是在fragment,所以使用的是getActivity())
通過以上4步的設置操作,就可以完全解決android 7.0及以上許可權問題導致的崩潰問題。

⑦ android中怎麼創建一個表格

效果圖:


5、 單擊該項目,執行。便得到以上效果!

熱點內容
手機配置內存顏色怎麼看 發布:2025-02-05 12:47:36 瀏覽:198
電腦伺服器盤口順序 發布:2025-02-05 12:40:20 瀏覽:663
mcpe伺服器怎麼開 發布:2025-02-05 12:40:13 瀏覽:835
linux文件記錄 發布:2025-02-05 12:39:31 瀏覽:775
編程獎金計算 發布:2025-02-05 12:27:01 瀏覽:185
軟體怎麼去掉付費配置 發布:2025-02-05 12:26:17 瀏覽:504
安卓手機怎麼下載谷歌商店apk 發布:2025-02-05 12:21:09 瀏覽:367
騰訊視頻的緩存在哪裡 發布:2025-02-05 12:21:03 瀏覽:710
安卓聊天記錄未備份怎麼恢復 發布:2025-02-05 12:05:02 瀏覽:953
海外搭建伺服器可以連外網嗎 發布:2025-02-05 11:49:21 瀏覽:64