2011年8月4日 星期四

AdSense被取消

因為要寫收費的Android應用程式,所以必須申請
  1. Google Checkout
  2. AdSense
而申請AdSense需要一個網頁,因此,就依Google的建議,申請了一個blog

http://soundnetinternational.blogspot.com/

然後,向Google申請了一段廣告碼,放在程式後面。
<script type="text/javascript">
<!-- google_ad_client = "pub-2966378125551072";
/* 300x250, 已建立 2011/8/2 */
google_ad_slot = "2454852632";
google_ad_width = 300;
google_ad_height = 250; //-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
乖乖,用了一天,就被停掉了,然後,收到Google的來信:

您好:

如歡迎使用電子郵件所述,只要您開始放送廣告,我們就會開始審查您的 AdSense 帳戶。在我們最近一次審查中,您的帳戶因下列違規而被停權。

問題:
- Google AdSense Support
---------------------
詳細原因:
內容不夠豐富:若想符合加入 AdSense 的資格,您網站的文字內容必須充足;這樣一來,AdSense 專家才能進行審核,我們的檢索器也才能找出適合顯示在您網頁上的相關廣告。因此,我們建議您在網站上加入更多完整的文句和段落。根據規定,網站內容必須全面上線且正常運作,讓使用者能夠透過選單、網站導覽或適當的連結來瀏覽整個網站。只要您網站絕大部分都已完備且運作正常,我們會很樂意重審您的申請。

請注意,只有 AdSense 帳戶獲得核准的發佈商,才能使用 AdSense 網域廣告或 AdSense 網域廣告線上版。

所以,
  1. 網頁內容不足,申請AdSense就不過
  2. AdSense不過,Android Market就沒有辦法付帳
其實,現在也沒有什麼帳要收,而且台北市政府和Google鬧翻了,台灣也無法下載付費的軟體。而這個網頁才剛開,當然沒有什麼內容。自己也不想說什麼,遊戲規則既是如此,就寫一些內容放上去吧。Google這樣規定也無可厚非,但是,對於開發Android的開發商而言,一定要和AdSense綁在一起…..反正,老大說了就算。
  1. 2011/8/7 依Email所說,再提出升請,說,要一個星期或更久,FINE,就等等看。
  2. 2011/8/8 Email來函,授權通過,重新開放使用。
  3. 2011/8/9 重新申請了廣告碼,可是沒有連線上,出現以下訊息。上回剛開始也是如此,不過這次持續比較久。

Google為開發者提供的工具

放上市集的應用程式,在Google中有不錯的分析,會告訴你,有多少人正確安裝。這些人,使用那些廠牌的手機,使用那一個版本的作業系統,是從那一個區域(國家)下載,使用什麼語言。看到自己的程式被全世界使用,是一種快感。自己不是,也不想寫大家歡迎的應用程式,因此,也不期待一炮而紅,就希望,自己的努力,可以幫助一些人,實際、具體的幫助一些人。
自己的『我的禱告簿』,放上去15天,有45人使用。有趣。

static 的使用(在Android)

看自己放到Android Market上的程式,有錯誤報導。

java.lang.NullPointerException
at com.sni.Prayer.PrayerData.Update(PrayerData.java:148)
at com.sni.Prayer.WordActivity$6.onClick(WordActivity.java:173)
at android.view.View.performClick(View.java:2461)
at android.view.View$PerformClick.run(View.java:8888)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4627)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
at dalvik.system.NativeStart.main(Native Method)

而錯誤的程式是:

private static Cursor getWords(int prayerId) {
Cursor cursor=null;
try {
cursor = database.query(WORDS_TABLE, null, PRAYER_ID_IS+prayerId, null, null, null, null);
} catch(SQLException e) {
Log.e(TAG,e.getMessage());
}
return cursor;
}

Null Exception, 那當然就是database了,但是,為什麼如此? 執行到這一行,應該早就開啟了,為什麼沒有開啟呢? 當然,簡單加個 if(database==null) 的判斷是容易的,我的想法是,一定要找出原因,而且,不必執行的就不必執行,不要執行重覆的判斷。經過分析,發現:
自己很喜歡用static 變數,覺得
  1. 當作global來用
  2. 不必new就可以使用
  3. 只有一份記憶體,省空間
這個database類別,被自己搞成,每一個member都是static, 自己覺得不妥,但是懶得改,真是糟糕,給自己一個警惕。如果都是static, 沒有instance, 是否在記憶體管理時,會被整個御載,御載後再載入時,其static data member的值是否就沒了,變成null了,當然,改成每次讀的時候都check,這是一個方式,但是,這麼多了許多判斷。因此,保留data member 為static, 但是要使用這個資料的函數,都取消,不是static, 保留那些完全沒有使用資料成員的函數為static。每個Activity在調用時,必須先創建(new)一個instance。而在Constructor中加入判斷資料庫database是否為空的判斷。…OK
同時也想到,記憶體管理是以class, Activity為單位。因此,保留每一個類別的”完全”獨立,有助於記憶體管理。因此,將所有static改成private, 或是移到不是Activity的class中。

連續變換方向導致記憶體不足

發現自己開發的Android軟體,有一個問題。如果頻煩地更換方向,一下直,一下橫,多幾次,系統就出現異常錯誤。
因此,就追蹤了一下,直/橫的變化,系統作了些什麼? 結果發現,方向變化時,系統會執行onCreate(), 自己在onCreate中,讀數據庫,讀出列表,顯示在螢幕上,列表中,每一列又有圖標,這麼一下,來來回回,不斷重做,讀出的記憶體,沒有及時釋放,(自己一直不想去控制GC, Garbage Collection, 自己的程式還不是那麼大,殺雞焉用牛刀),結果系統就當掉了,因此,就想,那讀出來的資料就不要再讀了,做過的事,就不要再做了,因此,
  1. 第一個想到,就將變數移成class member, 在讀出來前,先判斷一下,是否為null, 若是null再重讀。
  2. 乖乖,結果相同,想當然耳,會執行onCreate(), 自然是重新產生一個instance,所有的data member當然被reset了,那怎麼辦?
  3. 那就用static,將這個變數鎖住,測試,對了正常了,怎麼變換都不會當了,只有第一次做,以後就不做了。可是有一個問題要處理。每一次啟動這個Activity, 變數將會保留上一個,也不會重新讀取(如果每次需要讀不同的資料)。
  4. 那把清空的動作加在onStop()好了,在結束時設為null, 下回進來就會重讀。測試,結果方向變化也會執行onStop()…..怎麼辦?
  5. 那試onDestroy()好了,方向變換應該不會進行”破壞”吧!很不幸地,方向變換時,依然執行了onDestroy(), 那怎麼辦?
  6. 這麼基本的需求,一定有解。結果發現,在onDestroy()中加上isFinishing()的判斷就好了,如果是方向變換,isFinishing()會傳回false, 如果是結束,就會傳回true。(在onStop()中做,應該也是可以的,想Stop()未必結束,只要沒有Destroy應該都不必重讀,放在onDestroy()中比較省功。

@Override
protected void onDestroy() {
/*
* When you change orientation, onDestroy will be invoked too.
*/
super.onDestroy();
if(this.isFinishing()) {
files=null;
index=0;
bm=null;
}
}

讀Bitmap導致記憶體不足

做一個Android應用程式, 可以更換不同的圖片, 以選擇一幅自己要的, 採用ImageSwitch, 經歷了幾個階段
  1. 一開始, 我使用Dialog來選擇圖片, 可以總選擇了幾個以後, 就出現異常錯誤, 後來用程式碼抓了一下, 發現是記憶體不足的錯誤。

    try {
    bm = BitmapFactory.decodeFile(files[index]);
    imvPickVisual.setImageBitmap(bm);
    } catch(Exception e) {
    Log.e(TAG,e.getMessage());
    } catch(OutOfMemoryError e) {
    Log.e(TAG,e.getMessage());
    }

    原來,有catch(Exception e), 並不能捕捉出記憶體不夠的錯誤,加了 catch(OutOfMemoryError e) 後,補捉到了這個錯誤,然而,為什麼如此呢?
  2. 第一個念頭是,會不會在Dialog中實現,在複雜了,導致錯誤? 因此就改用Activity來現實,在辛苦了一下之後,Dialog沒有了,直接啟動Activity,結果還是一樣。
    接著想,是否是ImageSwitcher的問題,自己用這個元件來實現,這個元件一開始調用了makeView()函數(而且被調用了兩次), 然後圖片轉換時,就不再調用了,是否ImageSwitcher必須要配合Gallery來使用? 因此就加上Gallery好了,結果在2.2版本中,左側的元件表是有Gallery, 但是,放入layout後,系統就說,找不到這個類別(class),必須重新定義,但是改成2.3版就正常了,既然這個元件在2.2版中有定義,也就不必自己在2.2來一回。
  3. 接著想,那改成ImageView好了,反正就只有簡單的,左劃下一張,右劃上一張,如此而己。又辛苦了一下,把ImageSwitcher改成ImageView, 結果仍然相同,選了幾張以後,就Out of memory了。
  4. 再爬一下網路,發現,原來罪魁禍首是 decodeFile(), 這個函數將整個圖片抓下來,釋放沒有及時,幾張圖後,記憶體就完了。要在decode時,加上scale, 而scale最好是2的指數,運算會比較快,實際decode的圖,會是原來圖的1/scale。因此,
    1. 先讀出圖的尺寸
    2. 決定scle
    3. 依scale來decode

    public static Bitmap decodeFile(File file, int max_size) {
    Bitmap bm = null;
    try {
    //Check the image size to be decode, to decide sample size
    BitmapFactory.Options op = new BitmapFactory.Options();
    op.inJustDecodeBounds = true;
    FileInputStream isFile = new FileInputStream(file);
    BitmapFactory.decodeStream(isFile, null, op);
    isFile.close();
    int scale = 1;
    if (op.outHeight > max_size || op.outWidth > max_size) {
    scale = (int) Math.pow(2, (int) Math.round(Math.log(max_size / (double) Math.max(op.outHeight, op.outWidth)) / Math.log(0.5)));
    }

    //Decode with inSampleSize
    op = new BitmapFactory.Options();
    op.inSampleSize = scale;
    isFile = new FileInputStream(file);
    bm = BitmapFactory.decodeStream(isFile, null, op);
    isFile.close();
    } catch(Exception e) {
    Log.e(TAG, e.getMessage());
    }
    return bm;
    }

2011年8月3日 星期三

我的願景板上線了!

昨天,我的願景板上線了。這是我的第一個收費的應用程式,收費0.99美元。這個錢,只有一點點,是跨越公益與商業的界線。因為台北政府為了保護台灣民眾,所以台灣民眾不能下載收費的應用程式。所以,大家目前仍然不能下載使用。
願景,是我們人生的指標。
願景,是讓我們好還要更好的幫助。
願景,是讓我們走過人生低谷的良藥。
願景,是讓我們喜樂的泉源。
願景,是讓我們奮鬥的力量。
願景,是讓我們心連心,手牽手,共同分享、只同努力的紅線。
許多人,在成功後,每下愈況。
許多人,在失敗後,一蹶不振。
許多人,痛苦渡日,怨天尤人。
就是因為,沒有好好地看顧好自己的願景。
因此,就想寫一個程式,隨身可用。
可以記錄、追蹤、實現,我們每一個願景。


新河願景板
我的禱告簿





新河願景板
我們有更詳細的使用說明,如果你有須要較為完整的使用說明,請Email給我,我會回傳給你。
faust.dehng@gmail.com


程式一開始, 會列出所有願景項目。你可以點選後進入該禱告項目之內容。底色表示願景相關的人生領域(身、心、靈、家、公、財、事),可以多選。底色將依所選的領域混色。
整個軟體在操作上的原則就是:
1.按{選單}(Menu)鍵可以調用其他的選項。
2.改什麼就點選什麼。
3.按{回復}(Back)鍵可以回上層選單。
4.在願景清單中,長按,可以有更多的選項。
資料會放在SD卡內 soundnet路徑中。


在願景清單中,點選,可以看到願景的詳細內容,每一個願景可以配一張視覺化圖像,顯示在左上角,如果點選,可以放大這張視覺化圖片,如果長按,可以更改這張視覺化圖片。你可以預先將圖片,放在SD卡內soundnet/picture路徑下,如此,在選擇上就會比較方便。目前提供選擇圖片(長按圖片),會有
1. 拍照的路徑 (SD卡內 DCIM 路徑)
2. 預設圖片路徑 (SD卡內 soundnet/picture)
3. SD卡之根路徑
供選擇,選持圖片時,可以
1. 往左劃/ 往右劃。
2. 點選這一張。

2011年7月20日 星期三

我的禱告簿上架了

第一個開發的Android軟體,我的禱告簿,正式在Android Market上架了,就在今天,大約九點這個時候。
人生,就是一個一直往前走的過程。
從DOS時代,到Windows 3.1, NT, XP, Vista, System 7.....一路跟,很辛苦。
從Fortran, Pascal, Cobol, VB, C, C++, C#, Java....一路學,很辛苦。
從語音系統開發,網頁程式開發,網路系統開發到手機程式開發....一路做,很辛苦。
從自以為是,自負自滿,到歷盡滄桑,反求諸已....一路改,很辛苦。

很辛苦,也很喜樂。
喜樂的是,今是而昨非,生命在改變。
將來會怎麼變,就交託給神了。
就是要努力,要反省,要謙卑,要悔改。

就是要常常喜樂,不住禱告,凡事謝恩。


我的禱告簿





我的禱告簿
如果你有須要較為完整的使用說明,請Email給我,我會回傳給你。faust.dehng@gmail.com


程式一開始, 會列出所有禱告項目。你可以點選後進入該禱告項目之內容。顯示黃色的底是代表『為人代禱』。
整個軟體在操作上的原則就是:
1.按{選單}(Menu)鍵可以調用其他的選項。
2.改什麼就點選什麼。
3.按{回復}(Back)鍵可以回上層選單。
資料會放在SD卡內 soundnet路徑中。