2017年5月25日 星期四
2017年2月16日 星期四
[Android] UnsatisfiedLinkError
症狀
E/AndroidRuntime: FATAL EXCEPTION: Thread-3056
Process: com.perfectcorp.beautycircle, PID: 14917
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.perfectcorp.beautycircle-1/base.apk"],nativeLibraryDirectories=[/data/app/com.perfectcorp.beautycircle-1/lib/arm64, /data/app/com.perfectcorp.beautycircle-1/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]] couldn't find "libweibosdkcore.so"
at java.lang.Runtime.loadLibrary(Runtime.java:367)
at java.lang.System.loadLibrary(System.java:1076)
at com.sina.weibo.sdk.net.HttpManager.(SourceFile:83)
at com.sina.weibo.sdk.net.NetUtils.internalHttpRequest(SourceFile:46)
at com.sina.weibo.sdk.utils.AidTask.loadAidFromNet(SourceFile:344)
at com.sina.weibo.sdk.utils.AidTask.access$3(SourceFile:331)
at com.sina.weibo.sdk.utils.AidTask$2.run(SourceFile:203)
at java.lang.Thread.run(Thread.java:818)
線索
- 這問題發生在 Nexus 9 的機器上,HTC Nexus 9 是一台 ARM64 的機器
- java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.perfectcorp.beautycircle-1/base.apk"],nativeLibraryDirectories=[/data/app/com.perfectcorp.beautycircle-1/lib/arm64, /data/app/com.perfectcorp.beautycircle-1/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]] couldn't find "libweibosdkcore.so"
解法
- Root cause 在於 Nexus 9 是 ARM64 的機器,但 libweibosdkcore.so 卻只有 x86。
- 為了解決這問題,解法會是指定 .so 的目錄
- 在 app gradle 的 defaultConfig 加上 ndk { abiFilters "armeabi-v7a" }
- 在 project的根目錄下,找到 gradle.properties (不存在則新建)"android.useDeprecatedNdk=true 指定目錄",不讓系统自動找.so檔
Reference
2012年1月9日 星期一
[C++] Mutex
1. 同一個時間內只能夠有一個執行緒擁有mutex。
2. 同一個時間內只能夠有一個執行緒進入critical section。
3. Mutex速度較慢。因為Critical Section不需要進入OS核心,直接在User Mode 就可以進行動作。
4. Mutex可以跨Process使用。Critical Section則只能夠在同一個Process使用。
5. 等待一個mutex時,你可以指定『結束等待』時間長度,但對於critical section則不行。
用CreateMutex來產生Mutex
HANDLE WINAPI CreateMutex( __in_opt LPSECURITY_ATTRIBUTES lpMutexAttributes, __in BOOL bInitialOwner, __in_opt LPCTSTR lpName ); // The name can have a "Global\" or "Local\" prefix to explicitly create the object in the global or session namespace. The remainder of the name can contain any character except the backslash character (\).用ReleaseMutex來釋放Mutex
BOOL WINAPI ReleaseMutex( __in HANDLE hMutex );簡單的範例
CMutex::CMutex(std::wstring wstrMutexName)
{
std::wstring wstrMutex = L"Global\\";
wstrMutex += wstrMutexName;
m_mutex = ::CreateMutex(NULL, FALSE, wstrMutex.c_str());
}
CMutex::~CMutex()
{
::CloseHandle(m_mutex);
}
HRESULT CMutex::Lock()
{
HRESULT hr = E_FAIL;
DWORD dRet = WaitForSingleObject(m_mutex, INFINITE);
switch (dRet)
{
case WAIT_TIMEOUT: // Time out
break;
case WAIT_OBJECT_0: // Process over
hr = S_OK;
break;
case WAIT_OBJECT_0 + 1: // Don't Know
break;
}
return hr;
}
void CMutex::Unlock()
{
::ReleaseMutex(m_mutex);
}
如果想要利用Mutex讓一個Process在同一時間只被執行一次的話,可以用下列的方法檢查1.Mutex的Create是否有Error
CMutex g_vthumbMutex(strModuleName);
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
return;
}
2. 用OpenMutex
HANDLE WINAPI OpenMutex( __in DWORD dwDesiredAccess, __in BOOL bInheritHandle, __in LPCTSTR lpName );Reference:
[C++] Enumerate file in directory.
bool findFileInDir(wchar_t* rootDir)
{
wchar_t fname[BUFFSIZE];
ZeroMemory(fname, BUFFSIZE);
WIN32_FIND_DATA fd;
ZeroMemory(&fd, sizeof(WIN32_FIND_DATA));
HANDLE hSearch;
wchar_t filePathName[BUFFSIZE];
wchar_t tmpPath[BUFFSIZE];
ZeroMemory(filePathName, BUFFSIZE);
ZeroMemory(tmpPath, BUFFSIZE);
wcscpy(filePathName, rootDir);
BOOL bSearchFinished = FALSE;
if( filePathName[wcslen(filePathName) -1] != L'\\' )
{
wcscat(filePathName, L"\\");
}
wcscat(filePathName, L"*");
hSearch = FindFirstFile(filePathName, &fd);
//Is directory
if( (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
&& wcscmp(fd.cFileName, L".") && wcscmp(fd.cFileName, L"..") )
{
wcscpy(tmpPath, rootDir);
wcscat(tmpPath, fd.cFileName);
wcscat(tmpPath, L"\\");
findFileInDir(tmpPath);
}
else if( wcscmp(fd.cFileName, L".") && wcscmp(fd.cFileName, L"..") )
{
wsprintf(fname, L"%ls%ls", rootDir, fd.cFileName);
DEBUG_MSG((L"[findFileInDir] filename: %ls", fname));
}
while( !bSearchFinished )
{
if( FindNextFile(hSearch, &fd) )
{
if( (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
&& wcscmp(fd.cFileName, L".") && wcscmp(fd.cFileName, L"..") )
{
wcscpy(tmpPath, rootDir);
wcscat(tmpPath, fd.cFileName);
wcscat(tmpPath, L"\\");
findFileInDir(tmpPath);
}
else if( wcscmp(fd.cFileName, L".") && wcscmp(fd.cFileName, L"..") )
{
wsprintf(fname, L"%ls%ls", rootDir, fd.cFileName);
DEBUG_MSG((L"[findFileInDir] filename: %ls", fname));
}
}
else
{
if( GetLastError() == ERROR_NO_MORE_FILES ) //Normal Finished
{
bSearchFinished = TRUE;
}
else
bSearchFinished = TRUE; //Terminate Search
}
}
FindClose(hSearch);
return true;
}
[C++] How to get free disk space
int getFreeSpace(wstring wstrWorkingFolder)
{
int nFreeSpace = 0;
HINSTANCE hModule = ::LoadLibrary(L"KERNEL32.DLL");
if(hModule)
{
PFNGETDISKFREESPACEEX pDiskFreeSpaceEx = NULL;
// Determine function to use
pDiskFreeSpaceEx = reinterpret_cast(::GetProcAddress(hModule, "GetDiskFreeSpaceExA"));
if(!pDiskFreeSpaceEx)
{
DWORD dwSectorsPerCluster = 0;
DWORD dwBytesPerSector = 0;
DWORD dwFreeClusters = 0;
DWORD dwClusters = 0;
if(::GetDiskFreeSpace(wstrWorkingFolder.c_str(), &dwSectorsPerCluster, &dwBytesPerSector, &dwFreeClusters, &dwClusters) == TRUE)
{
nFreeSpace = dwFreeClusters * dwBytesPerSector * dwSectorsPerCluster;
}
}
else
{
ULARGE_INTEGER uliFreeBytesAvailableToCaller;
ULARGE_INTEGER uliTotalNumberOfBytes;
ULARGE_INTEGER uliTotalNumberOfFreeBytes;
if(::GetDiskFreeSpaceEx(wstrWorkingFolder.c_str(), &uliFreeBytesAvailableToCaller, &uliTotalNumberOfBytes, &uliTotalNumberOfFreeBytes) == TRUE)
{
nFreeSpace = static_cast<__int64>(uliFreeBytesAvailableToCaller.QuadPart / (1024 * 1024));
}
}
// Release library
::FreeLibrary(hModule);
}
return nFreeSpace;
}
[C++] Check Process
bool CheckProcess(wstring wstrExeName)
{
bool bHasProcess = false;
unsigned long aProcesses[1024], cbNeeded, cProcesses;
if(EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
{
cProcesses = cbNeeded / sizeof(unsigned long);
for(unsigned int i = 0; i < cProcesses; i++)
{
if(aProcesses[i] == 0)
continue;
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, aProcesses[i]);
wchar_t buffer[50];
GetModuleBaseNameW(hProcess, 0, buffer, 50);
CloseHandle(hProcess);
if(wstrExeName == wstring(buffer))
{
bHasProcess = true;
break;
}
}
}
return bHasProcess;
}
[C++] Kill Process
bool KillProcess(wstring wstrExename)
{
bool bOK = false;
WCHAR wszCmd[2048];
int nSize = 160;
PROCESS_INFORMATION pi;
STARTUPINFOW si;
ZeroMemory(&si, sizeof(STARTUPINFOW));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFOW);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
swprintf(wszCmd, L"taskkill /F /IM %s", wstrExename.c_str());
if (CreateProcessW(NULL, wszCmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE|IDLE_PRIORITY_CLASS, NULL, NULL, &si, &pi))
{
int nKillWaitTime = 30000;
int nRet = WaitForSingleObject(pi.hProcess, nKillWaitTime);
switch (nRet)
{
case WAIT_TIMEOUT: // Time out
break;
case WAIT_OBJECT_0: // Process over
break;
case WAIT_OBJECT_0 + 1: // Don't Know
break;
}
CloseHandle(pi.hProcess);
pi.hProcess = NULL;
CloseHandle(pi.hThread);
pi.hThread = NULL;
bOK = true;
}
return bOK;
}
[C++] Create Process
bool CreateProcess(wstring wstrProcessName, wstring wstrParas, )
{
WCHAR wszCmd [2048] = {0};
swprintf(wszCmd, L"%ls %ls", wstrProcessName.c_str(), wstrParas.c_str());
int nProcessPriority = BELOW_NORMAL_PRIORITY_CLASS;
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFOW));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFOW);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
if (CreateProcessW(NULL, wszCmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE|nProcessPriority, NULL, NULL, &si, &pi))
{
return true;
}
return false;
}
[C++] wstring to string
Here is an example for wstring to string transform.
HRESULT DSU_WcsToStr(wstring wstrSrc, string* strDst, UINT nCodepage)
{
_DSU_ChkPtrRtn(strDst);
int iStrLen = wstrSrc.length();
int nCount = WideCharToMultiByte(nCodepage, 0, wstrSrc.c_str(), iStrLen, NULL, 0, NULL, FALSE);
char* pCHAR = new char[nCount + 1];
nCount = WideCharToMultiByte(nCodepage, 0, wstrSrc.c_str(), iStrLen, pCHAR, nCount + 1, NULL, FALSE);
pCHAR[nCount] = NULL;
*strDst = pCHAR ;
delete [] pCHAR ;
return S_OK ;
}
[C++] string to wstring
Here is an example for string to wstring transform.
HRESULT DSU_StrToWcs( string strSrc, wstring* wstrDst, UINT nCodepage = CP_UTF8) ;
HRESULT DSU_StrToWcs(string strSrc, wstring* wstrDst, UINT nCodepage)
{
if ( wstrDst == NULL )
return E_POINTER;
int iStrLen = strSrc.length() ;
int nCount = MultiByteToWideChar(nCodepage, 0, strSrc.c_str(), iStrLen, NULL, 0) ;
WCHAR* pWCHAR = (WCHAR*)alloca(sizeof(WCHAR) * (nCount + 1)) ;
nCount = MultiByteToWideChar(nCodepage, 0, strSrc.c_str(), iStrLen, pWCHAR, nCount+1) ;
pWCHAR[nCount] = NULL ;
*wstrDst = pWCHAR ;
return S_OK ;
}
2011年10月27日 星期四
[Android] expired debug certificate
http://developer.android.com/resources/faq/troubleshooting.html#signingcalendar
I can't compile my app because the build tools generated an expired debug certificate
If your development machine uses a locale that has a non-Gregorian calendar, you may encounter problems when first trying to compile and run your application. Specifically, you may find that the Android build tools won't compile your application because the debug key is expired.
The problem occurs because the Keytool utility — included in the JDK and used by the Android build tools — fails to properly handle non-Gregorian locales and may create validity dates that are in the past. That is, it may generate a debug key that is already expired, which results in the compile error.
If you encounter this problem, follow these steps to work around it:
- First, delete the debug keystore/key already generated by the Android build tools. Specifically, delete the
debug.keystore file. On Linux/Mac OSX, the file is stored in ~/.android. On Windows XP, the file is stored in C:\Documents and Settings\\.android . On Windows Vista, the file is stored in C:\Users\\.android
- Next, you can either
- Temporarily change your development machine's locale (date and time) to one that uses a Gregorian calendar, for example, United States. Once the locale is changed, use the Android build tools to compile and install your app. The build tools will regenerate a new keystore and debug key with valid dates. Once the new debug key is generated, you can reset your development machine to the original locale.
- Alternatively, if you do not want to change your machine's locale settings, you can generate the keystore/key on any machine using the Gregorian calendar, then copy the
debug.keystore file from that computer to the proper location on your development machine.
This problem has been verified on Windows and may apply to other platforms.
For general information about signing Android applications, see Signing Your Applications.
2011年9月28日 星期三
[Android] Dynamically change Options Menu Items in Android
Sometimes, we need to change options menu dynamically, you can follow this example.
Reference:
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear();
// You can use mEnableMenu to enable meun
if (mEnableMenu)
{
menu.add(0, 0, 0, "Setting");
}
return super.onPrepareOptionsMenu(menu);
}
Reference:
[Android] How to monitor sdcard mount/unmout?
In this example, we demo the sdcard mount/unmount monitor.
public void onCreate() {
super.onCreate();
this.registerReceiver(this.myReceiver, getSDCardIntentFilter());
}
public IntentFilter getSDCardIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
intentFilter.addDataScheme("file");
return intentFilter;
}
private BroadcastReceiver myReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED))
{
// handle SD card unmounted
}
else if (action.equals(Intent.ACTION_MEDIA_MOUNTED))
{
// handle SD card mounted
}
}
};
[Android] How to block back and search button on Dialog?
It will cancel dialog If user press back or search button on dialog. Therefore, sometimes we need to block back and search button on dialog to ensure the logic of dialog can work normally.
public void alertDialog(final String title, final String msg, final String btn1, final String btn2, final String btn3, final String cmd, final String data)
{
AlertDialog.Builder MyAlertDialog = new AlertDialog.Builder(self);
MyAlertDialog.setIcon(android.R.drawable.ic_dialog_alert);
MyAlertDialog.setTitle(title);
MyAlertDialog.setMessage(msg);
MyAlertDialog.setCancelable(false); // block back button
MyAlertDialog.setOnKeyListener(new DialogInterface.OnKeyListener()
{
@Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event)
{
if (keyCode==KeyEvent.KEYCODE_SEARCH) // block search button
return true;
else
return false;
}
});
DialogInterface.OnClickListener OkClick = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Handle onClick
}
};
if (btn1 != null)
MyAlertDialog.setPositiveButton(btn1, OkClick );
if (btn2 != null)
MyAlertDialog.setNegativeButton(btn2, OkClick );
if (btn3 != null)
MyAlertDialog.setNeutralButton(btn3, OkClick );
MyAlertDialog.show();
}
[Android] How to create status bar notification?
To create a status bar notification:
1. Get a reference to the NotificationManager:
2. Instantiate the Notification:
3. Define the Notification's expanded message and Intent:
4. Pass the Notification to the NotificationManager:
Reference:
1. Get a reference to the NotificationManager:
String ns = Context.NOTIFICATION_SERVICE; NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
2. Instantiate the Notification:
int icon = R.drawable.notification_icon; CharSequence tickerText = "Hello"; long when = System.currentTimeMillis(); Notification notification = new Notification(icon, tickerText, when);
3. Define the Notification's expanded message and Intent:
Context context = getApplicationContext(); CharSequence contentTitle = "My notification"; CharSequence contentText = "Hello World!"; Intent notificationIntent = new Intent(this, MyClass.class); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
4. Pass the Notification to the NotificationManager:
private static final int HELLO_ID = 1; mNotificationManager.notify(HELLO_ID, notification); That's it. Your user has now been notified.
Reference:
2011年9月16日 星期五
[Android] How can I refresh MediaStore on Android?
Sometimes, we cannot get the correct information from MediaStore after file add or delete.
We need to do MediaStore refresh to make MediaStore parsing the information instantly.
Reference:
package com.roryok.MediaRescan;
import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
public class MediaRescan extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + Environment.getExternalStorageDirectory())));
setContentView(R.layout.main);
}
//Rescan the sdcard after copy the file
private void rescanSdcard() throws Exception{
Intent scanIntent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + Environment.getExternalStorageDirectory()));
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
intentFilter.addDataScheme("file");
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + Environment.getExternalStorageDirectory())));
}
}
Reference:
2011年9月2日 星期五
[JavaScript] How to format string?
String.format = function(src){
if (arguments.length == 0)
return null;
var args = Array.prototype.slice.call(arguments, 1);
return src.replace(/\{(\d+)\}/g, function(m, i)
{
return args[i];
});
};
Demo:
msg will be "This is variable A and variable B.
var template = "This is variable {0} and variable {1}";
var variable1 = "A";
var variable2 = "B";
var msg = String.format(template, variable1, variable2);
[Java] How to check if a directory is not empty?
This is a example to demo how to check if a directory is not empty?
Reference:
package org.kodejava.example.io;
import java.io.File;
public class EmptyDirCheck {
public static void main(String[] args) {
File file = new File("/home/username/data");
//
// Check to see if the object represent a directory.
//
if (file.isDirectory()) {
//
// Get list of file in the directory. When its length is not zero
// the folder is not empty.
//
String[] files = file.list();
if (files.length > 0) {
System.out.println("The " + file.getPath() + " is not empty!");
}
}
}
}
Reference:
2011年8月31日 星期三
[JavaScript] How to auto truncate text?
Sometimes we need to auto truncate text when the width of text division is not enough to fit the text. For example, we have a text like "Hello, I am a long long long text." But the text division only can show "Hello, I am a long long". I this case, the last long could not be shown normal. To ensure text presentation normally in screen, we need a function to calculate the width of input text and auto truncate text with tail token (ex: ...).
JavaScript code:
Usage:
References:
JavaScript code:
function autoEllipseText(element, text, width)
{
element.innerHTML = '' + text + '';
inSpan = document.getElementById('ellipsisSpan');
if(inSpan.offsetWidth > width)
{
var i = 1;
inSpan.innerHTML = '';
while(inSpan.offsetWidth < (width) && i < text.length)
{
inSpan.innerHTML = text.substr(0,i) + '...';
i++;
}
returnText = inSpan.innerHTML;
element.innerHTML = '';
return returnText;
}
return text;
}
Usage:
function setCellText()
{
cellElement = document.getElementById('textCell');
textBoxElement = document.getElementById('cellText');
cellElement.innerHTML =
autoEllipseText(cellElement, textBoxElement.value, 80);
}
References:
2011年6月2日 星期四
[Java] function如何傳不定參數
private void MyParameterizedFunction(String param1, int param2, Boolean... params) {
assert params.length <= 1;
boolean param3 = params.length > 0 ? params[0].booleanValue() : false;
}
private void test()
{
MyParameterizedFunction("", 1);
}
Reference:
訂閱:
文章 (Atom)