Android
SyncAdapter z okresową synchronizacją danych
Szukaj…
Wprowadzenie
Komponent adaptera synchronizacji w aplikacji obudowuje kod zadań, które przesyłają dane między urządzeniem a serwerem. W oparciu o harmonogram i wyzwalacze podane w aplikacji środowisko adaptera synchronizacji uruchamia kod w komponencie adaptera synchronizacji.
Ostatnio pracowałem nad SyncAdapter i chcę dzielić się swoją wiedzą z innymi, może to pomóc innym.
Synchronizuj adapter z każdą minimalną wartością żądającą od serwera.
<provider
android:name=".DummyContentProvider"
android:authorities="sample.map.com.ipsyncadapter"
android:exported="false" />
<!-- This service implements our SyncAdapter. It needs to be exported, so that the system
sync framework can access it. -->
<service android:name=".SyncService"
android:exported="true">
<!-- This intent filter is required. It allows the system to launch our sync service
as needed. -->
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<!-- This points to a required XML file which describes our SyncAdapter. -->
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>
<!-- This implements the account we'll use as an attachment point for our SyncAdapter. Since
our SyncAdapter doesn't need to authenticate the current user (it just fetches a public RSS
feed), this account's implementation is largely empty.
It's also possible to attach a SyncAdapter to an existing account provided by another
package. In that case, this element could be omitted here. -->
<service android:name=".AuthenticatorService"
>
<!-- Required filter used by the system to launch our account service. -->
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<!-- This points to an XMLf ile which describes our account service. -->
<meta-data android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
Ten kod należy dodać w pliku manifestu
W powyższym kodzie mamy usługę synchronizacji oraz dostawcę i dostawcę uwierzytelnienia.
W aplikacji musimy utworzyć pakiet xml, aby dodać pliki XML synchronizatora i uwierzytelniacza. hentator.xml
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="@string/R.String.accountType"
android:icon="@mipmap/ic_launcher"
android:smallIcon="@mipmap/ic_launcher"
android:label="@string/app_name"
/>
syncadapter
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="@string/R.String.contentAuthority"
android:accountType="@string/R.String.accountType"
android:userVisible="true"
android:allowParallelSyncs="true"
android:isAlwaysSyncable="true"
android:supportsUploading="false"/>
Authenticator
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.os.Bundle;
public class Authenticator extends AbstractAccountAuthenticator {
private Context mContext;
public Authenticator(Context context) {
super(context);
this.mContext=context;
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse, String s) {
return null;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse, String s, String s1, String[] strings, Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public String getAuthTokenLabel(String s) {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String[] strings) throws NetworkErrorException {
return null;
}
}
AuthenticatorService
public class AuthenticatorService extends Service {
private Authenticator authenticator;
public AuthenticatorService() {
super();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
IBinder ret = null;
if (intent.getAction().equals(AccountManager.ACTION_AUTHENTICATOR_INTENT)) ;
ret = getAuthenticator().getIBinder();
return ret;
}
public Authenticator getAuthenticator() {
if (authenticator == null)
authenticator = new Authenticator(this);
return authenticator;
}
}
IpDataDBHelper
public class IpDataDBHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION=1;
private static final String DATABASE_NAME="ip.db";
public static final String TABLE_IP_DATA="ip";
public static final String COLUMN_ID="_id";
public static final String COLUMN_IP="ip";
public static final String COLUMN_COUNTRY_CODE="country_code";
public static final String COLUMN_COUNTRY_NAME="country_name";
public static final String COLUMN_CITY="city";
public static final String COLUMN_LATITUDE="latitude";
public static final String COLUMN_LONGITUDE="longitude";
public IpDataDBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, DATABASE_NAME, factory, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
String CREATE_TABLE="CREATE TABLE " + TABLE_IP_DATA + "( " + COLUMN_ID + " INTEGER PRIMARY KEY ,"
+ COLUMN_IP + " INTEGER ," + COLUMN_COUNTRY_CODE + " INTEGER ," + COLUMN_COUNTRY_NAME +
" TEXT ," + COLUMN_CITY + " TEXT ," + COLUMN_LATITUDE + " INTEGER ," + COLUMN_LONGITUDE + " INTEGER)";
sqLiteDatabase.execSQL(CREATE_TABLE);
Log.d("SQL",CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TABLE_IP_DATA);
onCreate(sqLiteDatabase);
}
public long AddIPData(ContentValues values)
{
SQLiteDatabase sqLiteDatabase =getWritableDatabase();
long insertedRow=sqLiteDatabase.insert(TABLE_IP_DATA,null,values);
return insertedRow;
}
public Cursor getAllIpData()
{
String[] projection={COLUMN_ID,COLUMN_IP,COLUMN_COUNTRY_CODE,COLUMN_COUNTRY_NAME,COLUMN_CITY,COLUMN_LATITUDE,COLUMN_LONGITUDE};
SQLiteDatabase sqLiteDatabase =getReadableDatabase();
Cursor cursor = sqLiteDatabase.query(TABLE_IP_DATA,projection,null,null,null,null,null);
return cursor;
}
public int deleteAllIpData()
{
SQLiteDatabase sqLiteDatabase=getWritableDatabase();
int rowDeleted=sqLiteDatabase.delete(TABLE_IP_DATA,null,null);
return rowDeleted;
}
}
Główna aktywność
public class MainActivity extends AppCompatActivity {
private static final String ACCOUNT_TYPE="sample.map.com.ipsyncadapter";
private static final String AUTHORITY="sample.map.com.ipsyncadapter";
private static final String ACCOUNT_NAME="Sync";
public TextView mIp,mCountryCod,mCountryName,mCity,mLatitude,mLongitude;
CursorAdapter cursorAdapter;
Account mAccount;
private String TAG=this.getClass().getCanonicalName();
ListView mListView;
public SharedPreferences mSharedPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.list);
mIp=(TextView)findViewById(R.id.txt_ip);
mCountryCod=(TextView)findViewById(R.id.txt_country_code);
mCountryName=(TextView)findViewById(R.id.txt_country_name);
mCity=(TextView)findViewById(R.id.txt_city);
mLatitude=(TextView)findViewById(R.id.txt_latitude);
mLongitude=(TextView)findViewById(R.id.txt_longitude);
mSharedPreferences=getSharedPreferences("MyIp",0);
//Using shared preference iam displaying values in text view.
String txtIp=mSharedPreferences.getString("ipAdr","");
String txtCC=mSharedPreferences.getString("CCode","");
String txtCN=mSharedPreferences.getString("CName","");
String txtC=mSharedPreferences.getString("City","");
String txtLP=mSharedPreferences.getString("Latitude","");
String txtLN=mSharedPreferences.getString("Longitude","");
mIp.setText(txtIp);
mCountryCod.setText(txtCC);
mCountryName.setText(txtCN);
mCity.setText(txtC);
mLatitude.setText(txtLP);
mLongitude.setText(txtLN);
mAccount=createSyncAccount(this);
//In this code i am using content provider to save data.
/* Cursor cursor=getContentResolver().query(MyIPContentProvider.CONTENT_URI,null,null,null,null);
cursorAdapter=new SimpleCursorAdapter(this,R.layout.list_item,cursor,new String []{"ip","country_code","country_name","city","latitude","longitude"},
new int[] {R.id.txt_ip,R.id.txt_country_code,R.id.txt_country_name,R.id.txt_city,R.id.txt_latitude,R.id.txt_longitude},0);
mListView.setAdapter(cursorAdapter);
getContentResolver().registerContentObserver(MyIPContentProvider.CONTENT_URI,true,new StockContentObserver(new Handler()));
*/
Bundle settingBundle=new Bundle();
settingBundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL,true);
settingBundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED,true);
ContentResolver.requestSync(mAccount,AUTHORITY,settingBundle);
ContentResolver.setSyncAutomatically(mAccount,AUTHORITY,true);
ContentResolver.addPeriodicSync(mAccount,AUTHORITY,Bundle.EMPTY,60);
}
private Account createSyncAccount(MainActivity mainActivity) {
Account account=new Account(ACCOUNT_NAME,ACCOUNT_TYPE);
AccountManager accountManager=(AccountManager)mainActivity.getSystemService(ACCOUNT_SERVICE);
if(accountManager.addAccountExplicitly(account,null,null))
{
}else
{
}
return account;
}
private class StockContentObserver extends ContentObserver {
@Override
public void onChange(boolean selfChange, Uri uri) {
Log.d(TAG, "CHANGE OBSERVED AT URI: " + uri);
cursorAdapter.swapCursor(getContentResolver().query(MyIPContentProvider.CONTENT_URI, null, null, null, null));
}
public StockContentObserver(Handler handler) {
super(handler);
}
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(syncStaredReceiver, new IntentFilter(SyncAdapter.SYNC_STARTED));
registerReceiver(syncFinishedReceiver, new IntentFilter(SyncAdapter.SYNC_FINISHED));
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(syncStaredReceiver);
unregisterReceiver(syncFinishedReceiver);
}
private BroadcastReceiver syncFinishedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Sync finished!");
Toast.makeText(getApplicationContext(), "Sync Finished", Toast.LENGTH_SHORT).show();
}
};
private BroadcastReceiver syncStaredReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Sync started!");
Toast.makeText(getApplicationContext(), "Sync started...", Toast.LENGTH_SHORT).show();
}
};
}
MyIPContentProvider
public class MyIPContentProvider extends ContentProvider {
public static final int IP_DATA=1;
private static final String AUTHORITY="sample.map.com.ipsyncadapter";
private static final String TABLE_IP_DATA="ip_data";
public static final Uri CONTENT_URI=Uri.parse("content://" + AUTHORITY + '/' + TABLE_IP_DATA);
private static final UriMatcher URI_MATCHER= new UriMatcher(UriMatcher.NO_MATCH);
static
{
URI_MATCHER.addURI(AUTHORITY,TABLE_IP_DATA,IP_DATA);
}
private IpDataDBHelper myDB;
@Override
public boolean onCreate() {
myDB=new IpDataDBHelper(getContext(),null,null,1);
return false;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
int uriType=URI_MATCHER.match(uri);
Cursor cursor=null;
switch (uriType)
{
case IP_DATA:
cursor=myDB.getAllIpData();
break;
default:
throw new IllegalArgumentException("UNKNOWN URL");
}
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
int uriType=URI_MATCHER.match(uri);
long id=0;
switch (uriType)
{
case IP_DATA:
id=myDB.AddIPData(contentValues);
break;
default:
throw new IllegalArgumentException("UNKNOWN URI :" +uri);
}
getContext().getContentResolver().notifyChange(uri,null);
return Uri.parse(contentValues + "/" + id);
}
@Override
public int delete(Uri uri, String s, String[] strings) {
int uriType=URI_MATCHER.match(uri);
int rowsDeleted=0;
switch (uriType)
{
case IP_DATA:
rowsDeleted=myDB.deleteAllIpData();
break;
default:
throw new IllegalArgumentException("UNKNOWN URI :" +uri);
}
getContext().getContentResolver().notifyChange(uri,null);
return rowsDeleted;
}
@Override
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
return 0;
}
}
SyncAdapter
public class SyncAdapter extends AbstractThreadedSyncAdapter {
ContentResolver mContentResolver;
Context mContext;
public static final String SYNC_STARTED="Sync Started";
public static final String SYNC_FINISHED="Sync Finished";
private static final String TAG=SyncAdapter.class.getCanonicalName();
public SharedPreferences mSharedPreferences;
public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
this.mContext=context;
mContentResolver=context.getContentResolver();
Log.i("SyncAdapter","SyncAdapter");
}
@Override
public void onPerformSync(Account account, Bundle bundle, String s, ContentProviderClient contentProviderClient, SyncResult syncResult) {
Intent intent = new Intent(SYNC_STARTED);
mContext.sendBroadcast(intent);
Log.i(TAG, "onPerformSync");
intent = new Intent(SYNC_FINISHED);
mContext.sendBroadcast(intent);
mSharedPreferences =mContext.getSharedPreferences("MyIp",0);
SharedPreferences.Editor editor=mSharedPreferences.edit();
mContentResolver.delete(MyIPContentProvider.CONTENT_URI,null,null);
String data="";
try {
URL url =new URL("https://freegeoip.net/json/");
Log.d(TAG, "URL :"+url);
HttpURLConnection connection=(HttpURLConnection)url.openConnection();
Log.d(TAG,"Connection :"+connection);
connection.connect();
Log.d(TAG,"Connection 1:"+connection);
InputStream inputStream=connection.getInputStream();
data=getInputData(inputStream);
Log.d(TAG,"Data :"+data);
if (data != null || !data.equals("null")) {
JSONObject jsonObject = new JSONObject(data);
String ipa = jsonObject.getString("ip");
String country_code = jsonObject.getString("country_code");
String country_name = jsonObject.getString("country_name");
String region_code=jsonObject.getString("region_code");
String region_name=jsonObject.getString("region_name");
String zip_code=jsonObject.getString("zip_code");
String time_zone=jsonObject.getString("time_zone");
String metro_code=jsonObject.getString("metro_code");
String city = jsonObject.getString("city");
String latitude = jsonObject.getString("latitude");
String longitude = jsonObject.getString("longitude");
/* ContentValues values = new ContentValues();
values.put("ip", ipa);
values.put("country_code", country_code);
values.put("country_name", country_name);
values.put("city", city);
values.put("latitude", latitude);
values.put("longitude", longitude);*/
//Using cursor adapter for results.
//mContentResolver.insert(MyIPContentProvider.CONTENT_URI, values);
//Using Shared preference for results.
editor.putString("ipAdr",ipa);
editor.putString("CCode",country_code);
editor.putString("CName",country_name);
editor.putString("City",city);
editor.putString("Latitude",latitude);
editor.putString("Longitude",longitude);
editor.commit();
}
}catch(Exception e){
e.printStackTrace();
}
}
private String getInputData(InputStream inputStream) throws IOException {
StringBuilder builder=new StringBuilder();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
//String data=null;
/*Log.d(TAG,"Builder 2:"+ bufferedReader.readLine());
while ((data=bufferedReader.readLine())!= null);
{
builder.append(data);
Log.d(TAG,"Builder :"+data);
}
Log.d(TAG,"Builder 1 :"+data);
bufferedReader.close();*/
String data=bufferedReader.readLine();
bufferedReader.close();
return data.toString();
}
}
SyncService
public class SyncService extends Service {
private static SyncAdapter syncAdapter=null;
private static final Object syncAdapterLock=new Object();
@Override
public void onCreate() {
synchronized (syncAdapterLock)
{
if(syncAdapter==null)
{
syncAdapter =new SyncAdapter(getApplicationContext(),true);
}
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return syncAdapter.getSyncAdapterBinder();
}
}
Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow