[iOS][Android]工作上的一些雜事筆記20141128

好像變成週記?不過~因為只是草草的紀錄一下,工作上有關的問題或是看到的網路介紹,並沒有打算寫成詳細的文章.

// 當兩個thread同時呼叫以下的指令,在網路不穩的狀況下會出問題.
InetAddress.getByName("SOME_DOMAIN_NAME").isReachable(30); 

// 比較建議的方式
public boolean isOnline() {
    ConnectivityManager cm =
        (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo netInfo = cm.getActiveNetworkInfo();
    return netInfo != null && netInfo.isConnectedOrConnecting();
}

// 必須要注意到加到 Manifest
< uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

[Mooc][Android]Programming Mobile Aplication on Android Platform -Final Peer Assignmement

image

前言:

最後一個作業是要做出一個自拍管理的App透過Content Provider來管理資料,還附有通知.從無到有其實很有趣的.我相信也能學到更多的東西,我希望能夠全部寫完,加油了.

筆記:

  • 關於使用App Action Bar會強制請你加入Android-support-V7的project.
    • 如果你是要讓Android V7~V10的App支援Action Bar,你必須要新增Android-support-V7的project並且新增相依專案.但是如果希望新增一個然後不要有這些的相依性問題的話,你需要利用以下設定建立.(同時符合大部分作業的需求在V18上)
      • Minimal Requirement V17
      • Target V19
      • Compitible with V18
    • 參考文章:
  • 如果要找default camera 在action menu的icon
  • 裡面主要透過Content Provider 的方式來達到資料的存取,並且增加了Notification的功能.
    • Alarm Manager 透過他呼叫起一個Intent然後來設定Notification.透過這個方式來達到,離開App之後,可以在固定時間後收到通知.
  • 關於讀取camera所拍照回來的照片檔案位址:
//指定時間作為檔名來另存新檔
PackageManager pm = getPackageManager();
if (pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
	Intent intent_camera = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
	
	SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
    String currentTimeStamp = dateFormat.format(new Date());
    File mFile = new File(Environment.getExternalStorageDirectory(), currentTimeStamp+".jpg");
    Uri outputFileUri = Uri.fromFile(mFile);
	intent_camera.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, outputFileUri);
	Toast.makeText(getBaseContext(), "File URI is "+outputFileUri, Toast.LENGTH_LONG).show();
	startActivityForResult(intent_camera, 0);
}
else {
	Toast.makeText(getBaseContext(), "Camera is not available", Toast.LENGTH_LONG).show();
}
  • 讀取縮圖並且直接設定到ListView裡面:
    • 關於這部分的我把它想得太複雜了,網路上不少作法推薦直接把檔案放進content provider裡面,但是這樣沒有辦法存取多個資料.
    • 最後其實就直接記住檔名(這裡檔名使用當下的時間,最後把圖片放進去BitmapDrawable就可以.
// 從content provide拿取檔案名稱
String photoContentUri = cursor.getString(cursor.getColumnIndex(DataContract.PIC_FILE_NAME));
// 與系統位置配對成完整檔案位置
File mFile = new File(Environment.getExternalStorageDirectory(), photoContentUri);

if (null != photoContentUri) {
    InputStream input = null;
    try {
    // 讀取縮圖
    	input = context.getContentResolver().openInputStream(Uri.fromFile(mFile));
    	if (input != null) {
    		photoBitmap = new BitmapDrawable(mApplicationContext.getResources(), input);
    		photoBitmap.setBounds(0, 0, mBitmapSize, mBitmapSize);
    	}
    } catch (FileNotFoundException e) {
    	Log.e(TAG, "FileNotFoundException");
    }
}
  • 由於點下去還要顯示完整大圖,這邊我也想得太複雜了.我以為還需要再增加一個新的activity,其實只要使用Intent就可以了
    //抓取圖片後,顯示新的Activity
    File mFile = new File(Environment.getExternalStorageDirectory(), fileName);
    Intent intent = new Intent();
    intent.setAction(android.content.Intent.ACTION_VIEW);
    intent.setDataAndType(Uri.fromFile(mFile), "image/jpg");
    startActivity(intent);

參考鏈結:

[iOS][Android]工作上的一些雜事筆記20141120

許多相關雜事紀錄,盡量條列式記起來.

  • [綜合]
  • [iOS]
    • HealthKit的請求權限(requestAuthorizationToShareTypes)必須在一開始就要要求.
      • 如果有需要新增,或是減少,只是重新執行App是沒有用的.需要重新安裝才會跑到正確的集合.
      • 它類似GameCenter一樣,當你試著移除App的時候.會詢問你要不要把相關的數據全部移除.
    • WatchKit也放出來了,幾件事情值得記起來.
      • WatchKit類似App Extension(Today Widget)類似,可能需要手機App作為互動本體.
  • [MacOSX]
    • 本來想試著找找MacOSX上面的FileMerge工具(個人習慣使用Araxis Merge).但是太貴了,後來用習慣Xcode裡面的FileMerge也就放棄繼續找了.
  • [MSFT]
    • 微軟在2014/11/13第二天的Connect Developer Event 裡面,做了重大的宣布
      • 開放.Net Framework 原始碼,範圍包括CLR,JIT(Just-In-Time Compiler),GC與.NET基本的一些Framework.
      • 目前開放只有XPATH與Collection,喜愛用.NET裡面XML物件的可以去看看.hub.com/dotnet/corefx
    • 微軟也宣布可以內建支援Docker,並且有放出原始碼給大家用.這篇文章有更多資訊.

相關資料:

[Mooc][Android]Programming Mobile Aplication on Android Platform(week8) - Data Management

image

前言:

最後一週,分成兩個一個是Assignment.另外還有Peer Assignment,要努力堅持到最後.

筆記:

關於資料管理的部分:

  • SharePreferences 屬於小量的資料存取,(iOS 使用的是NSUserDefault 與 pList來管理)
    • 讀取資料getInt getString
    • 寫入資料必須先取得SharedPreferences.Editor 之後再setInt or setString
  • PreferenceFragment 是一個UI Fragment可以存取與直接收到SharedPreferences 的變化.
    • 透過setContentView可以把一個表示資料與介面的xml檔案設定好,然後存取它所代表的資料.
  • File 可以使用 internal memory 或是external memory
    • 其中比較需要注意的是MODE_PRIVATE會把檔案鎖定成這個App才能讀取或是只用同樣的user ID跟App.
    • 對於external storage需要注意以下的事項:
      • 對於external memory 的檔案需要在manifest上面增加相關的權限:
      • 存取前要先注意是否有讀或寫的權限:
        • Environment .getExternalStorageState()
  • SQL (SQLlite)
    • 如果要繼承SQLiteOpenHelper來使用,必須要Override OnCreate跟OnUpgrade.
    • 資料庫的檔案在 /data/data//databases/
    • 透過以下方式可以讀取SQL資料
      • adb -s emulator-5554 shell
      • sqlite3 /data/data
// SharedPreferences

final SharedPreferences prefs = getPreferences(MODE_PRIVATE);
//get data from SharedPreferences
int high_score = prefs.getInt("high_score", 0);

//set data to SharedPreferences
SharedPreferences.Editor editor = prefs.edit();
editor.putInt("high_score", 99);
editor.commit();

//File read sample (internal storage)
FileInputStream fis = openFileInput(fileName);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));

String line = "";

while (null != (line = br.readLine())) {

	TextView tv = new TextView(this);
	tv.setTextSize(24);
	tv.setText(line);

	ll.addView(tv);

}
br.close();

//File write
FileOutputStream fos = openFileOutput(fileName, MODE_PRIVATE);

PrintWriter pw = new PrintWriter(new BufferedWriter(
		new OutputStreamWriter(fos)));

pw.println("Line 1: This is a test of the File Writing API");
pw.println("Line 2: This is a test of the File Writing API");
pw.println("Line 3: This is a test of the File Writing API");

pw.close();


//File read sample (external storage)
if (Environment.MEDIA_MOUNTED.equals(Environment
		.getExternalStorageState())) {

	File outFile = new File(
			getExternalFilesDir(Environment.DIRECTORY_PICTURES),
			fileName);
	
	if (!outFile.exists())
		copyImageToMemory(outFile);
	
	ImageView imageview = (ImageView) findViewById(R.id.image);
	imageview.setImageURI(Uri.parse("file://" + outFile.getAbsolutePath()));
		}

關於內容提供者的部分(Content Provider)

  • Content Provider提供可以存取有結構的資料,可以跨app來存取.
  • Content Resolver可以讓你去存取content provider裡面的資料.其中透過URI來傳遞與定位資料的相關資訊.
// Sample to using ContentResolver and URI
// Contact data
String columnsToExtract[] = new String[] { Contacts._ID,
		Contacts.DISPLAY_NAME, Contacts.PHOTO_THUMBNAIL_URI };

// Get the ContentResolver
ContentResolver contentResolver = getContentResolver();

// filter contacts with empty names
String whereClause = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
		+ Contacts.DISPLAY_NAME + " != '' ) AND (" + Contacts.STARRED
		+ "== 1))";

// sort by increasing ID
String sortOrder = Contacts._ID + " ASC";

// query contacts ContentProvider
Cursor cursor = contentResolver.query(Contacts.CONTENT_URI,
		columnsToExtract, whereClause, null, sortOrder);

// pass cursor to custom list adapter
setListAdapter(new ContactInfoListAdapter(this, R.layout.list_item,
		cursor, 0));
  • 其中要利用content provider去變更你Google的通訊錄的範例需要注意以下項目:
    • 在manifest裡面,取得
    • 透過Google Account Manager取得帳戶資訊來讀取資料.
    • 透過ContentProviderOperation 來新增一個系列的batch執行序列(batch operation)
    • 透過content resolver來執行執行序列 (getContentResolver().applyBatch())
// Get Account information
// Must have a Google account set up on your device
String mAccountList = AccountManager.get(this).getAccountsByType("com.google");
String mType = mAccountList[0].type;
String mName = mAccountList[0].name;

// Set up a batch operation on Contacts ContentProvider
ArrayList batchOperation = new ArrayList();

for (String name : mNames) {
int position = ops.size();

// First part of operation
batchOperation(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
		.withValue(RawContacts.ACCOUNT_TYPE, mType)
		.withValue(RawContacts.ACCOUNT_NAME, mName)
		.withValue(Contacts.STARRED, 1).build());

// Second part of operation
batchOperation(ContentProviderOperation.newInsert(Data.CONTENT_URI)
		.withValueBackReference(Data.RAW_CONTACT_ID, position)
		.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
		.withValue(StructuredName.DISPLAY_NAME, name).build());
}

try {

	// Apply all batched operations
	getContentResolver().applyBatch(ContactsContract.AUTHORITY,
			batchOperation);

} catch (RemoteException e) {
	Log.i(TAG, "RemoteException");
} catch (OperationApplicationException e) {
	Log.i(TAG, "RemoteException");
}
</pre>

- 剛剛關於content provider的範例都是使用已經架設的好的content provider.如果要新增自己的個人的content provider需要透過以下的流程:
    - 新增相關權限在 mannifest裡面: 新增 < provider /> 在< application /> 內
    - 建立 ContentProvider 衍生類別
    - 設定好唯一的URI之後,就可以透過這個URI使用ContentResolver來取得資訊.


關於Service的部分:

- 關於Service類別:
    - 沒有使用者介面
    - 兩個主要用途
        - 可以執行背景程序     
        - 跨App間的互動部分
- 關於IntentService
    - 將Service像是Intent一樣使用,建立後會在App背景等待呼叫
    - 透過Intent 來設定,但是透過startService而不是startActivity來啟動
- 關於與遠端的service溝通方式:
    - 使用messenger
        - 透過messanger來與service傳遞與接受訊息
    - 使用AIDL (Android Interface Definition Language)
        - 先宣告interface在 .aidl file
        - 不論是service與user都需要有建立 .aidl 檔案
        - 在Server端:
            - 需要建立與stub相關的API
        - 在User端:
            - 在ServiceConnection取得Service stub本體
            - 在App使用stub本身去呼叫他的API
            - 結束的時候必須要使用onServiceDisconnected清除stub    
    - Android team有建議要小心使用AIDL如果是在multiple thread的部分下面,或許可以考慮使用Messenger[http://developer.android.com/guide/components/bound-services.html](http://developer.android.com/guide/components/bound-services.html)            


// Intent Service sampl
public class LoggingService extends IntentService {

	public static String EXTRA_LOG = "course.examples.Services.Logging.MESSAGE";
	private static final String TAG = "LoggingService";

	public LoggingService() {
		super(TAG);
	}

	@Override
	protected void onHandleIntent(Intent intent) {

		Log.i(TAG, intent.getStringExtra(EXTRA_LOG));

	}
}


// 設定Intent
Intent startServiceIntent = new Intent(getApplicationContext(),
		LoggingService.class);

// 把需要log的資訊透過 putExtra 傳給Intent
startServiceIntent.putExtra(LoggingService.EXTRA_LOG,
		"Log this message");

// 啟動service
startService(startServiceIntent);

**關於作業:(2014/11/22更新)** - Cursor 做任何讀取的時候,需要知道他是不是empty - Cursor.moveToFirst 這時候才會傳回 cursor是不是 empty 直接檢查指標是不是null 是沒有用的 - 參考: [http://developer.android.com/reference/android/database/Cursor.html](http://developer.android.com/reference/android/database/Cursor.html) - 這次官方給的作業有bug,需要改manifest才會出現 action bar menu - 修改: - <activity android:theme="@style/Theme.AppCompat.Light" ... > 在Manifest - 這個讓我找了好久..... - 參考: [http://developer.android.com/guide/topics/ui/actionbar.html](http://developer.android.com/guide/topics/ui/actionbar.html) - 其實最困難的部分就是swapcursor的部分(笑),我想是因為註解太多敘述了,其實就專心地把swapcursor該做的做就好.陷阱題....... **參考網址:** - Android Storage Option(包含 SharedPreferences,Internal Storage,External Storage跟SQl) - [http://developer.android.com/guide/topics/data/data-storage.html](http://developer.android.com/guide/topics/data/data-storage.html) - Android about bound services - [http://developer.android.com/guide/components/bound-services.html](http://developer.android.com/guide/components/bound-services.html) - Android how to use cursor - [http://developer.android.com/reference/android/database/Cursor.html](http://developer.android.com/reference/android/database/Cursor.html) - Android about action bar menu - [http://developer.android.com/guide/topics/ui/actionbar.html](http://developer.android.com/guide/topics/ui/actionbar.html)

[Android學習]心血來潮把Android SDK更新(ADT 23)所帶來的問題與解決

image

前言:

週末前剛好心血來潮更新了Android SDK,也看到了ADT有了23版,並且當然也有下載最新的Android L 的SDK.想不到下載完之後造成Eclipse完全無法使用,利用了週末的時間好好的把系統整理了一次.(決定重灌,並且也要重新安裝gradle)

接下來就記錄一下,那些部分有遇到問題.希望能幫助到一樣遇到問題的你.以下這篇文章有更清楚地解釋出現了22.6更換到23版的錯誤

錯誤與修復:

image

  • 有關更新到Android SDK 23的錯誤
    • 錯誤狀況:
      • “This Android SDK requires Android Developer Toolkit version 23.0.0 …“並且重開也沒有效過.當然Check for update 也沒出現任何可以更新
    • 解決方法:
      • 根據一些網友的討論,似乎在[Install New Software]-> update SDK to 23.0
      • 但是我都會出現更新的dependency 出錯,根據這篇文章似乎從2014的六月到現在都還沒解決.所以我還是決定重灌Android Developer Toolkit
  • 有關更新到ADT 23之後,ADB 無法順利的執行(如果你有使用Genymotion 23.1之前的版本的話)
    • 錯誤狀況:
      • Genymotion 模擬器執行之後,Eclipse一直無法正確的編譯程式.出現 “The connection to adb is down, and a severe error has occured”
      • 到 ANDROID_SDK/sdk/platform-tools/adb 去跑 ./adb kill-server 與 ./adb start-server 也無法成功.
    • 解決方法:
      • 這邊有兩個解決方法,一個是把你的Genymotion更新到23.1
      • 另外一個就是依照以下這篇文章去改變genymotion的設定不使用自己的Adb而使用Android SDK的adb

參考資料:

[Mooc][Python][Week8]終於要完成了..... 完成最後的太空船射擊遊戲

image

前言與心得:

前一次的作業雖然說是要做一個太空船射擊隕石的遊戲,但是僅僅是完成前半段.也就是只有完成船的移動,發射飛彈與隕石的移動部分.接下來就要做碰撞跟多個隕石的控制.

總算把這個課程弄完了,最後的作業其實讓我寫起來很過癮.這大概也是為什麼我除了平常晚上努力寫之外.週末還要拿時間來寫. 主要是最由透過去寫一個遊戲,來完成對於物件導向與多個物件的管理.

比如說,你要如何管理一個子彈飛行的時間跟是否有撞擊到目標.其實只要管理好一個,到多個的時候自然水到渠成.一步步慢慢地完成每個單獨的類別,然後看到多個物件瞬間可以完成的成就感真不錯.

筆記:

這一個章節主要是講解 List,Set與 Dictionary.而重心主要在Set上面.

  • 關於List
    • 資料可以重複
  • 關於Set
    • 不會重複
    • 在逐一讀取(iteration)的時候,不應該做新增跟移除的動作,會造成索引混亂(也就是會讀錯個或是少讀)
      • 一般的作法就是另外列出一個集合後,利用差集合(difference_update)去運算.
      • 其中要注意difference不會改變原來的集合元素,而difference_update會.
    • 其中對於原來集合會有變更mutate的函式,有以下:
      • add/remove/modify/update(但是s.update(s)不會更改)
      • 兩個集合操作要有_update的函式
def get_rid_of(inst_set, starting_letter):
    #透過另外一個集合來搜集所有要刪除的
    remove_set = set([])
    for inst in inst_set:
        if inst[0] == starting_letter:
            remove_set.add(inst)
    #透過difference_update來移出所有在remove_set裡面的元素            
    inst_set.difference_update(remove_set)

作業:

其實上次不小心好像把這個禮拜的作業都弄完了.不過還是有一些細節需要微調:

  • 由於這是一個太空船射擊遊戲,主要就是一個飛船在螢幕上可以發射飛彈來射擊每秒隨機產生的隕石.
    • 所以原先的做法是寫了許多的函式來處理撞擊與子彈飛行的部分,這先部分要拆解到原先飛船與隕石各自的類別來處理.
    • 對於個別物件的參數取得,原本做法比較粗糙就是直接取得.這部分得都改由函式來取得.
  • 整個架構上,出題的人希望你透過兩個類別就能完成整個遊戲.一個類別是飛船,另外一個則是sprite.而不論是隕石,子彈或是爆炸三者都被視為是sprite的一個部分.
    • 其中就必須要區分出來這三者哪些有生命週期,比如子彈跟爆炸必須要多久之後消失,而隕石不需要.
    • 哪些屬於動畫需要改變畫圖的image