'3. Implementation/Android'에 해당되는 글 51건
- 2014.07.24 The process of the hierarchy dump in android
- 2014.07.24 How to search android source using google
- 2014.07.24 Android Source Search Site
- 2012.05.14 Android App (Apk) Source Reverse Engineering
- 2012.05.10 [펌] 항상 최상위에 나오는 뷰 만들기
- 2012.05.08 Classpath problem using Eclipse for Android platform development
- 2012.05.08 [펌] 안드로이드 - LockScreen 만들기
- 2012.02.16 emma 와 ant 를 이용하여 코드 커버리지 측정하기 1
- 2011.11.24 안드로이드 터치 이벤트 처리 관련
- 2011.11.06 Context 메모리 릭 관련
- 2011.11.01 이클립스에서 안드로이드 소스 보기 1
- 2011.09.10 How to Sign Android APK or Zip Files
- 2011.08.17 Understanding security on Android
- 2011.08.17 [펌] 이클립스에서 네이티브 애플리케이션을 디버그 모드로 서명하기
- 2011.08.17 Eclipse IDE를 이용한 Android Platform Development (CDT install)
- 2011.08.17 Android에서 Home Key 와 Power(Endcall) Key 를 onKeyDown, onKeyUp 으로 인식할 수 없는 이유
- 2011.07.28 Controlling the Embedded VM
- 2011.07.28 Stack Dumps
- 2011.07.28 [펌] Dalvik VM java assert 켜기
- 2011.07.28 [번역] Log 읽고 쓰기
The process of the hierarchy dump in android
The simple overview of ui dump process in android is like the following figure:
(2 years ago, I was involved in the project that developed the android test automation tool.)
The related source code is as below:
-----------------------------------------------------------------------------------
ViewServer.java
-----------------------------------------------------------------------------------
class ViewServer implements Runnable {
private final WindowManagerService mWindowManager;
class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener {
private Socket mClient;
private boolean mNeedWindowListUpdate;
private boolean mNeedFocusedWindowUpdate;
public ViewServerWorker(Socket client) {
mClient = client;
mNeedWindowListUpdate = false;
mNeedFocusedWindowUpdate = false;
}
public void run() {
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(mClient.getInputStream()), 1024);
final String request = in.readLine();
String command;
String parameters;
int index = request.indexOf(' ');
if (index == -1) {
command = request;
parameters = "";
} else {
command = request.substring(0, index);
parameters = request.substring(index + 1);
}
boolean result;
if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
result = writeValue(mClient, VALUE_PROTOCOL_VERSION);
} else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
result = writeValue(mClient, VALUE_SERVER_VERSION);
} else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
result = mWindowManager.viewServerListWindows(mClient);
} else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
result = mWindowManager.viewServerGetFocusedWindow(mClient);
} else if (COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
result = windowManagerAutolistLoop();
} else {
result = mWindowManager.viewServerWindowCommand(mClient,command, parameters);
}
// ...
}
}
}
-----------------------------------------------------------------------------------
WindowManagerService.java
-----------------------------------------------------------------------------------
https://android.googlesource.com/platform/frameworks/base/+/master/services/java/com/android/server/wm/WindowManagerService.java
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs,
DisplayManagerService.WindowManagerFuncs, DisplayManager.DisplayListener {
boolean viewServerWindowCommand(Socket client, String command, String parameters) {
// Any uncaught exception will crash the system process
try {
// Find the hashcode of the window
int index = parameters.indexOf(' ');
if (index == -1) {
index = parameters.length();
}
final String code = parameters.substring(0, index);
int hashCode = (int) Long.parseLong(code, 16);
// Extract the command's parameter after the window description
if (index < parameters.length()) {
parameters = parameters.substring(index + 1);
} else {
parameters = "";
}
final WindowState window = findWindow(hashCode);
if (window == null) {
return false;
}
data = Parcel.obtain();
data.writeInterfaceToken("android.view.IWindow");
data.writeString(command);
data.writeString(parameters);
data.writeInt(1);
ParcelFileDescriptor.fromSocket(client).writeToParcel(data, 0);
reply = Parcel.obtain();
final IBinder binder = window.mClient.asBinder();
// TODO: GET THE TRANSACTION CODE IN A SAFER MANNER
binder.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
}
}
-----------------------------------------------------------------------------------
WindowState.java
-----------------------------------------------------------------------------------
https://android.googlesource.com/platform/frameworks/base/+/master/services/java/com/android/server/wm/WindowState.java
import android.view.IWindow;
final class WindowState implements WindowManagerPolicy.WindowState {
final IWindow mClient;
}
-----------------------------------------------------------------------------------
IWindow.aidl
-----------------------------------------------------------------------------------
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/com/android/internal/view/BaseIWindow.java
oneway interface IWindow {
void executeCommand(String command, String parameters, in ParcelFileDescriptor descriptor);
}
-----------------------------------------------------------------------------------
ViewRootImpl.java
-----------------------------------------------------------------------------------
https://android.googlesource.com/platform/frameworks/base/+/17d28ca/core/java/android/view/ViewRootImpl.java
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
static class W extends IWindow.Stub {
@Override
public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
final View view = viewAncestor.mView;
if (view != null) {
if (checkCallingPermission(Manifest.permission.DUMP) !=
PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Insufficient permissions to invoke"
+ " executeCommand() from pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
}
OutputStream clientStream = null;
try {
clientStream = new ParcelFileDescriptor.AutoCloseOutputStream(out);
ViewDebug.dispatchCommand(view, command, parameters, clientStream);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (clientStream != null) {
try {
clientStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
}
}
-----------------------------------------------------------------------------------
ViewDebug.java
-----------------------------------------------------------------------------------
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewDebug.java
/**
* Various debugging/tracing tools related to {@link View} and the view hierarchy.
*/
class ViewDebug
{
void dispatchCommand(View view, String command, String parameters,
OutputStream clientStream) throws IOException {
if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
dump(view, false, true, clientStream);
}
/**
* Dumps the view hierarchy starting from the given view.
* @hide
*/
public static void dump(View root, boolean skipChildren, boolean includeProperties,
OutputStream clientStream) throws IOException {
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(clientStream, "utf-8"), 32 * 1024);
View view = root.getRootView();
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
dumpViewHierarchy(group.getContext(), group, out, 0,
skipChildren, includeProperties);
}
out.write("DONE.");
out.newLine();
} catch (Exception e) {
android.util.Log.w("View", "Problem dumping the view:", e);
} finally {
if (out != null) {
out.close();
}
}
}
private static void dumpViewHierarchy(Context context, ViewGroup group,
BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) {
if (!dumpView(context, group, out, level, includeProperties)) {
return;
}
if (skipChildren) {
return;
}
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
final View view = group.getChildAt(i);
if (view instanceof ViewGroup) {
dumpViewHierarchy(context, (ViewGroup) view, out, level + 1, skipChildren,
includeProperties);
} else {
dumpView(context, view, out, level + 1, includeProperties);
}
if (view.mOverlay != null) {
ViewOverlay overlay = view.getOverlay();
ViewGroup overlayContainer = overlay.mOverlayViewGroup;
dumpViewHierarchy(context, overlayContainer, out, level + 2, skipChildren,
includeProperties);
}
}
if (group instanceof HierarchyHandler) {
((HierarchyHandler)group).dumpViewHierarchyWithProperties(out, level + 1);
}
}
}
How to search android source using google
Use site option as below:
windowmanageservice site:android.googlesource.com/platform/frameworks/base/
Without cpp file
windowmanageservice site:android.googlesource.com/platform/frameworks/base/ -cpp
This search doesn't catch the contents of the source, so just use to reach latest or specific version of android source file.
Android Source Search Site
The following site is really helpful to find something inside android and kernel source!
This site uses OpenGrok internally. 2 years ago I also used this.
Additional References : Set Opengrok Up
Android App (Apk) Source Reverse Engineering
안드로이드 apk 파일에서 소스를 추출하는 방법을 설명해 드리겠습니다. 여기서 소스는 원본소스가 아닌 disassembly 를 이용하여 생성된 byte code (dex format)로부터 생성된 Java 소스를 말합니다. 좀더 정확하게 말하면 .class 포맷으로 변경해서 이 포맷을 볼 수 있는 Viewer 를 통해서 소스를 보는 것입니다. 아무래도 원본 소스랑은 좀 다르겠지만 로직이나 어떤 API 를 사용했느냐를 분석할 수 있어서 큰 도움이 될 수 있습니다.
(자료 자체가 공개할만한개 아니라 비공개로 작성합니다.)
유명한 SwypePad 의 apk 파일입니다. 엔지니어링 빌드 폰의 System/App 위치에서 복사했습니다.
1. http://code.google.com/p/dex2jar/ 에서 다운로드
2. d2j-dex2jar 명령을 실행합니다.
D:\Android\apps>d2j-dex2jar -f Swype.apk
dex2jar Swype.apk -> Swype-dex2jar.jar
3. 새로 생성된 Swype-dex2jar.jar 파일을 JD-GUI (Java Decompiler) 툴로 열면 아래처럼 소스가 보입니다.
http://java.decompiler.free.fr/?q=jdgui
[펌] 항상 최상위에 나오는 뷰 만들기
출처: http://blog.daum.net/_blog/BlogTypeView.do?blogid=0NADc#ajax_history_1
위 링크에는 최상위 윈도우를 만드는 방법에 대해서 소개하고 있습니다. 간단한 내용만 소개하므로 자세한 내용을 원하면 위 링크를 참고하세요.
1. 뷰 생성 및 최상위 윈도우에 추가
//최상위 윈도우에 넣기 위한 설정 WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, //항상 최 상위에 있게 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, //터치 인식 PixelFormat.TRANSLUCENT); //투명 WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); //윈도 wm.addView(tv, params); //최상위 윈도우에 뷰 넣기. permission필요.
2. 퍼미션 설정
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
3. 뷰 제거
((WindowManager) getSystemService(WINDOW_SERVICE)).removeView(tv);
다음은 like miller 님이 작성한 소스입니다.
원본 글 댓글에 보면 waa 님께서 SwipePad 에 대해서 소개하는 내용이 있습니다. 이 app 이 이와 유사한 기능을 이용하여 구현하고 있습니다. 아래는 SwipePad 캡처 화면입니다. 네 귀퉁이에 빨간색으로 표현된 부분이 모든 app 에서 인식되는 hotspot 영역입니다. 나름 재밌는 app 이더군요. 분석을 떠나서 한번 사용해 보심도 괜찮을듯 합니다.
웹서핑하다가 StackOverflow 에서 다음 링크를 발견하였습니다. 비슷한 내용이군요.
http://stackoverflow.com/questions/4481226/creating-a-system-overlay-always-on-top-button-in-android
Android 프레임워크 소스를 Eclipse 에 로딩시 Classpath 문제에 대한 해결책을 제공하는 글입니다.
에러 메시지
Project 'Generic-Android' is missing required library: 'out/target/ common/obj/JAVA_LIBRARIES/google-common_intermediates/javalib.jar' Project 'Generic-Android' is missing required library: 'out/target/ common/obj/JAVA_LIBRARIES/gsf-client_intermediates/javalib.jar' |
출처: https://groups.google.com/forum/#!topic/android-contrib/wEpciQkEUlA/discussion
It seems like the sample .classpath file from development/ide/eclipse
is outdated. Eclipse complains about some missing JAR libraries (out/
target/common/obj/JAVA_
javalib.jar and out/target/common/obj/JAVA_
client_intermediates/javalib.
that google-common_intermediates has been moved to android-
common_intermediates, and gsf-client_intermediates is not used
anymore.
After this, the build path errors were gone, but I got 197 errors
referring to missing types. Ouch! Since running make was hassle free,
this could only be another classpath problem for Eclipse. So I hunted
down the missing types, and after adding them to the .classpath file,
building was a success. Here are the extra libraries, that we have to
add to the .classpath file:
<classpathentry kind="lib" path="out/target/common/obj/
android-support-v13_
<classpathentry kind="lib" path="out/target/common/obj/
filterfw_intermediates/
Don't forget to refresh your project :)
[펌] 안드로이드 - LockScreen 만들기
출처: http://blog.naver.com/PostView.nhn?blogId=multikth&logNo=150109761153
☞ FC 포르테님이 작성한 글을 퍼왔습니다. 이렇게 복사해 오는 이유는 블로그 내에서 검색이 되도록 하기 위함입니다.
안드로이드폰에서 hold키를 누르면 순정 lockscreen으로 화면이 잠긴다.
이 락스크린을 개발자가 직접 건드릴수는 없으므로 가짜(?) 락스크린을 만들어 넣는 방법을 생각해 볼 수 있다.
LockScreen.java에서 Activity를 상속 받고,
onCreate메소드에서
WindowManager를 통해 플래그를 주었다...
- onCreate()
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED|
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout. .....);
위 플래그를 통해 홀드키를 누르면 안드로이드 순정 락스크린이 걸린상태에서 그 위에
LockScreen.java 액티비티를 띄우게 된다.
나는 서비스를 돌리고, 서비스에서 브로드캐스트 리시버를 통해 "Intent.ACTION_SCREEN_OFF" 브로드캐스트가 수신됐을때
(즉, 화면이 꺼졌을때) startActivity(new Intent(new, LockScreen.class)); 와 같은 형식으로 락스크린 액티비티를 호출해줬다.
그러면 화면이 꺼지고 안드로이드 순정 락스크린이 걸린 상태에서 그 위에 LockScreen.java 액티비티가 호출되어 있을 것이다.
이상태에서 다시 홀드키를 눌러 화면을 켜게되면 원래는 순정 락스크린이 떠 있어야 하지만, onCreate에서 추가해준 플래그에 의해
순정 락스크린이 떠있는 상태에서 그 위에 LockScreen.java 액티비티가 떠있게 되므로, 사용자 입장에서는
그것이 락스크린이 된다. 거기서 락을 해제할수있는 버튼을 하나 만들어서 그 버튼을 눌렀을때
LockScreen.java 액티비티는 onPause쯤에서 finish() 호출해서 액티비티를 종료 시키고,
아래 코드를 추가하면,
KeyguardManager km = (KeyguardManager)getSystemService(KEYGUARD_SERVICE);
KeyguardManager.KeyguardLock keyLock = km.newKeyguardLock(KEYGUARD_SERVICE);
keyLock.disableKeyguard(); //순정 락스크린 해제
이렇게 해주면 순정 LockScreen이 종료 되면서 순정 락스크린도 함께 해제되어
모르는사람은 그게 진짜 락스크린처럼 보일 것이다..
위에서 사용한 코드(KeyguardManager를 사용하려면 AndroidManifest.xml에서 퍼미션을 추가해줘야 사용 가능하다.
<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
반대로 락을 다시 걸어주는 방법은
keyLock.reenableKeyguard();를 호출해주면 순정락화면으로 잠긴다.
이때도 마찬가지, reenableKeyguard를 호출해 순정락화면으로 잠궈버리고,
다시 startActivity로 LockScreen.java액티비티를 호출하면 자연스럽게
onCreate에서 추가해준 플래그에 의해 락스크린위에 LockScreen.java 액티비티가 위로 올라가기 때문에
락스크린으로써의 기능(?)을 다 할수가 있다.
좀더 유연하게 쓰려면 여러가지 살을 좀 붙여야겠지만. 이정도만해도 충분히 락스크린을 만들 수 있다.
어떤 블로그나 자료를 보면 onCreate에서 추가한 플래그들이 락스크린을 해제 해주는 코드라고 하는데..
정확하게 말해서 락스크린을 해제 해주게 아니라, 락스크린이 안풀린 상태에서 그 위에 또다른 액티비티가 올라가기 때문에
락이 해제된 것 처럼 보일 뿐이다.
-혼자 공부하려고 쓴 포스트이니... 태클은 정중히 사양하겠습니다...
[출처] 안드로이드 - LockScreen 만들기|작성자 FC포르테
emma 와 ant 를 이용하여 코드 커버리지 측정하기
안드로이드 터치 이벤트 처리 관련
ViewRoot.java 에 보면 deliverPointerEvent 라는 함수가 있습니다. (비슷한 종류로 deliverKeyEvent, deliverKeyEventToViewHierarchy, deliverTrackballEvent 등이 더 있습니다.)
주의! 이 글에서 인용된 코드는 안드로이드 최신 소스가 아닐 수 있습니다.
private void deliverPointerEvent(MotionEvent event) { if (mTranslator != null) { mTranslator.translateEventInScreenToAppWindow(event); } boolean handled; if (mView != null && mAdded) { // enter touch mode on the down boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN; if (isDown) { ensureTouchMode(true); } if(Config.LOGV) { captureMotionLog("captureDispatchPointer", event); } if (mCurScrollY != 0) { event.offsetLocation(0, mCurScrollY); } if (MEASURE_LATENCY) { lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano()); } handled = mView.dispatchTouchEvent(event); if (MEASURE_LATENCY) { lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano()); } if (!handled && isDown) { int edgeSlop = mViewConfiguration.getScaledEdgeSlop(); final int edgeFlags = event.getEdgeFlags(); int direction = View.FOCUS_UP; int x = (int)event.getX(); int y = (int)event.getY(); final int[] deltas = new int[2]; if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) { direction = View.FOCUS_DOWN; if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) { deltas[0] = edgeSlop; x += edgeSlop; } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) { deltas[0] = -edgeSlop; x -= edgeSlop; } } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) { direction = View.FOCUS_UP; if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) { deltas[0] = edgeSlop; x += edgeSlop; } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) { deltas[0] = -edgeSlop; x -= edgeSlop; } } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) { direction = View.FOCUS_RIGHT; } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) { direction = View.FOCUS_LEFT; } if (edgeFlags != 0 && mView instanceof ViewGroup) { View nearest = FocusFinder.getInstance().findNearestTouchable( ((ViewGroup) mView), x, y, direction, deltas); if (nearest != null) { event.offsetLocation(deltas[0], deltas[1]); event.setEdgeFlags(0); mView.dispatchTouchEvent(event); } } } } }
View nearest = FocusFinder.getInstance().findNearestTouchable 에 보면 적절한 view 를 선택하고 있습니다. 해당 코드는 아래와 같습니다. (FocusFinder.java)
public View findNearestTouchable(ViewGroup root, int x, int y, int direction, int[] deltas) { ArrayList<View> touchables = root.getTouchables(); int minDistance = Integer.MAX_VALUE; View closest = null; int numTouchables = touchables.size(); int edgeSlop = ViewConfiguration.get(root.mContext).getScaledEdgeSlop(); Rect closestBounds = new Rect(); Rect touchableBounds = mOtherRect; for (int i = 0; i < numTouchables; i++) { View touchable = touchables.get(i); // get visible bounds of other view in same coordinate system touchable.getDrawingRect(touchableBounds); root.offsetRectBetweenParentAndChild(touchable, touchableBounds, true, true); if (!isTouchCandidate(x, y, touchableBounds, direction)) { continue; } int distance = Integer.MAX_VALUE; switch (direction) { case View.FOCUS_LEFT: distance = x - touchableBounds.right + 1; break; case View.FOCUS_RIGHT: distance = touchableBounds.left; break; case View.FOCUS_UP: distance = y - touchableBounds.bottom + 1; break; case View.FOCUS_DOWN: distance = touchableBounds.top; break; } if (distance < edgeSlop) { // Give preference to innermost views if (closest == null || closestBounds.contains(touchableBounds) || (!touchableBounds.contains(closestBounds) && distance < minDistance)) { minDistance = distance; closest = touchable; closestBounds.set(touchableBounds); switch (direction) { case View.FOCUS_LEFT: deltas[0] = -distance; break; case View.FOCUS_RIGHT: deltas[0] = distance; break; case View.FOCUS_UP: deltas[1] = -distance; break; case View.FOCUS_DOWN: deltas[1] = distance; break; } } } } return closest; }
위의 코드를 살펴 보면 ArrayList<View> touchables = root.getTouchables();
라는 부분이 있습니다. 다시 getTouchables 의 소스를 살펴보겠습니다.
아래는View.java 에서 발췌하였습니다.
/** * Find and return all touchable views that are descendants of this view, * possibly including this view if it is touchable itself. * * @return A list of touchable views */ public ArrayList<View> getTouchables() { ArrayList<View> result = new ArrayList<View>(); addTouchables(result); return result; } /** * Add any touchable views that are descendants of this view (possibly * including this view if it is touchable itself) to views. * * @param views Touchable views found so far */ public void addTouchables(ArrayList<View> views) { final int viewFlags = mViewFlags; if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) && (viewFlags & ENABLED_MASK) == ENABLED) { views.add(this); } }
addTouchables 는 ViewGroup.java 에서 다음과 같이 재정의되어 있습니다.
@Override public void addTouchables(ArrayList<View> views) { super.addTouchables(views); final int count = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < count; i++) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { child.addTouchables(views); } } }
위 내용에서 터치 대상이 되는 Target View 를 찾는 과정은 지금 진행하고 있는 일에서 꽤 중요한 정보가 될 것 같습니다.
Context 메모리 릭 관련
Activity 의 경우 Orientation 이 바뀌는 경우에도 소멸되고, 다시 생성됩니다. 이때 이전의 Activity Context 를 static 변수가 만일 참조하고 있다면 Activity 가 GC에 의해 소멸되지 않을 것입니다.
이렇게 Context 관련해서 참조를 해야 하는 경우는 Activity 보다는 Application Context 를 참조해야 합니다.
컨텍스트 관련 메모리 릭을 피하는 방법은 다음과 같습니다.
* Activity Context 를 참조하는 경우, Activity 라이프 사이클과 동일해야 합니다.
* context-activity 대신 context-application 을 사용하세요
* 중첩클래스(inner class)의 라이프 사이클을 제어하지 않는다면, activity 내에 비정적 중첩 클래스(non-static inner class)를 사용하지 마세요. 정적 중첩 클래스를 사용하고 내부적으로 activity 에 대한 약한 참조(weak reference)를 사용하세요. 이 이슈에 대한 해결책은 외부 클래스(outer class)에 대한 약한참조를 가지는 정적 중첩 클래스를 사용하는 것입니다, 이에 대한 예로 ViewRoot 안에 W 중첩 클래스가 그렇게 구현되어 있습니다.
* Garbage Collector 가 메모리 릭 방지를 보장해 주지는 않습니다.
출처: http://developer.android.com/resources/articles/avoiding-memory-leaks.html
이클립스에서 안드로이드 소스 보기
해당 버전의 소스를 하기 링크에서 원하는 버전의 Framework 소스를 다운받으시면 됩니다. 용량이 크지 않습니다. lol
http://grepcode.com/snapshot/repository.grepcode.com/java/ext/com.google.android/android/2.3.3_r1/
OS 버전과 API Number 는 Android 공식 사이트에 나와 있습니다. (http://source.android.com/source/build-numbers.html)
Code name | Version | API level |
---|---|---|
(no code name) | 1.0 | API level 1 |
(no code name) | 1.1 | API level 2 |
Cupcake | 1.5 | API level 3, NDK 1 |
Donut | 1.6 | API level 4, NDK 2 |
Eclair | 2.0 | API level 5 |
Eclair | 2.0.1 | API level 6 |
Eclair | 2.1 (incl. 2.1-update 1) | API level 7, NDK 3 |
Froyo | 2.2.x | API level 8, NDK 4 |
Gingerbread | 2.3 - 2.3.2 | API level 9, NDK 5 |
Gingerbread | 2.3.3 - 2.3.7 | API level 10 |
Honeycomb | 3.0 | API level 11 |
Honeycomb | 3.1 | API level 12 |
Honeycomb | 3.2 - 3.2.2 | API level 13 |
How to Sign Android APK or Zip Files
How to create private/public key pair using openssl (windows version)
- openssl genrsa -out key.pem 1024
- openssl req -new -key key.pem -out request.pem
- openssl x509 -req -days 9999 -in request.pem -signkey key.pem -out certificate.pem
- openssl pkcs8 -topk8 -outform DER -in key.pem -inform PEM -out key.pk8 -nocrypt
How to sign apk or zip files using SignApk.jar:
- java -jar signapk.jar certificate.pem key.pk8 your-app.apk your-signed-app.apk
OR
- java -jar signapk.jar certificate.pem key.pk8 your-update.zip your-signed-update.zip
참고
http://www.londatiga.net/how-to-sign-apk-zip-files/
http://android-dls.com/wiki/index.php?title=Generating_Keys
http://forum.xda-developers.com/showthread.php?t=442480&page=83
Understanding security on Android
참고: http://www.ibm.com/developerworks/opensource/library/x-androidsecurity/?ca=drs-
[펌] 이클립스에서 네이티브 애플리케이션을 디버그 모드로 서명하기
안드로이드 소스에 SRC/build/target/product/security 에 가면 eng 빌드에서 동작하는 테스트 키들이 있습니다. 이 테스트 키들은 user build 로 가면 각 제조사별로 다르게 Signing 되기 때문에 동작하지 않습니다.
- media.pk8, media.x509.pem
- platform.pk8, platform.x509.pem
- shared.pk8, shared.x509.pem
- testkey.pk8, testkey.x509.pem
4가지 키에 대한 간략한 설명은 다음과 같습니다.
* testkey - a generic key for packages that do not otherwise specify a key
* platform - a test key for packages that are part of the core platform
* shared - a test key for things that are shared in the home/contacts process
* media - a test key for packages that are part of the media/download system
jks 파일을 만드는 방법을 다음과 같습니다. (jks 파일은 이클립스에서 지정하여 자동으로 app 이 해당 키로 사이닝 되게 할 수 있습니다.)
1 PK8 유형의 키 파일을 PEM 유형 키로 변환
$ openssl pkcs8 -inform DER -nocrypt -in testkey.pk8 -out testkey.pem
2. 키와 인증서를 포함하는 PKCS#12 포맷의 저장소 생성
$ openssl pkcs12 -export -in testkey.x509.pem -inkey testkey.pem -out testkey.p12 -password pass:android -name androiddebugkey
3. PKCS#12 포맷 저장소 파일을 자바 키저장소 포맷으로 변환
(1) JDK 1.5 인 경우 jetty 라이브러리에 포함된 PKCS12Import 클래스를 이용
$ java -classpath jetty-core-6.1.14.jar org.mortbay.jetty.security.PKCS12Import testkey.p12 testkey.jks
(2) JDK 1.6 인 경우 JDK에서 제공하는 keytool 을 이용
$ keytool -importkeystore -deststorepass android -destkeystore testkey.jks -srckeystore testkey.p12 -srcstoretype PKCS12 -srcstorepass android☞ 윈도우즈에서도 key 를 생성할 수 있습니다.
☞ 이클립스 Preferences > Android > Build 메뉴 Custom debug keystore 에 jks 파일을 지정하면 해당 키를 이용하여 사이닝 되게 됩니다.
☞ 만약 platform 키로 사이닝된 System 권한의 App 을 만들려면 메니페스트에 sharedUserId 를 선언해 주어야 합니다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.sskk.sample" android:sharedUserId="android.uid.system" >
Eclipse IDE를 이용한 Android Platform Development (CDT install)
Controlling the Embedded VM
위 링크 원본 중 일부만 발췌하였습니다. 눈여겨 볼 것은 adb shell stop; adb shell start; 를 이용하는 것, /data/local.prop 파일의 존재, 그리고 아래 명령어 입력 방식.
adb shell "echo name = value >> /data/local.prop"
( 따옴표를 빠뜨리지 않아야 합니다!)
asb shell stop; adb shell start; 는 stdio, strerr 설정에서도 나옵니다.
$ adb shell stop $ adb shell setprop log.redirect-stdio true $ adb shell start
adb shell setprop <name> <value>
adb shell getprop <name>
<name> = <value>
(Hint: create a local.prop on your workstation, then adb push local.prop /data. Or, use one-liners like adb shell "echo name = value >> /data/local.prop" -- note the quotes are important.)
Stack Dumps
Like other desktop VMs, when the Dalvik VM receives a SIGQUIT (Ctrl-\ or kill -3
), it dumps stack traces for all threads. By default this goes to the Android log, but it can also be written to a file.
The dalvik.vm.stack-trace-file
property allows you to specify the name of the file where the thread stack traces will be written. The file will be created (world writable) if it doesn't exist, and the new information will be appended to the end of the file. The filename is passed into the VM via the -Xstacktracefile
argument.
For example:
adb shell setprop dalvik.vm.stack-trace-file /tmp/stack-traces.txt
If the property is not defined, the VM will write the stack traces to the Android log when the signal arrives.
출처: http://www.netmite.com/android/mydroid/dalvik/docs/embedded-vm-control.html#assertions
[펌] Dalvik VM java assert 켜기
##android device or emulator에서 java assert 켜기
모듈 통합에 있어서 어려운 점은 모듈에 버그가 있을 때 어떤 모듈에 있는지 찾는데 시간이 너무 많이 걸린다는 것이다. 버그가 발생했을때 빠르게 원인을 추적할 수 있는 방법 중에 하나는 assert를 효과적으로 사용하는 것이다. unit test의 testcase를 작성하여서 실행하는것도 중요한데 따로 케이스 작성할 여유가 없는 경우에는 java의 native assert를 이용할것을 적극 추천한다.
이방법은 에뮬레이터에서는 잘작동한다 그러나 실제 device에서는 root권한이 필요하므로 사용하지 못할 수 있다. (/data 경로에 파일을 생성해야한다.)
android의 Dalvik VM도 java native assert 기능을 제공한다. 다음은 assert를 켜는 방법이다.
1.file local.prop을 만들고 아래의 내용을 입력한다.
dalvik.vm.enableassertions=all
2.커맨드 창에서 다음을 실행한다.
adb push local.prop /data
제대로 전송 되었는지 확인한다.
adb shell ls -l /data/local.prop
3.device or emulator를 재부팅한다.
4. java 코드에서 assert(false);를 사용하여 잘 작동하는지 확인한다.
그 외 dalvik VM 속성 설정
dalvik.vm.enableassertions=all
dalvik.vm.stack-trace-file=/tmp/stack-traces.txt
dalvik.vm.deadlock-predict=err
dalvik.vm.checkjni=true
끄는 방법은 /data/local.prop파일을 삭제한 후 device를 재부팅하면 된다.
adb shell rm /data/local.prop
그외 Dalvik VM command line argument
adb shell dalvikvm -help
참고 문서
http://www.netmite.com/android/mydroid/dalvik/docs/embedded-vm-control.html#assertions
[번역] Log 읽고 쓰기
Log 클래스
Log 는 LogCat 에다가 메시지를 출력하기 위해 코드 내에 이용할 수 있는 로깅 클래스입니다. 일반적인 로깅 메소드는 다음과 같습니다.
v(String, String)
(verbose)d(String, String)
(debug)i(String, String)
(information)w(String, String)
(warning)e(String, String)
(error)
예를 들어,
Log.i("MyActivity", "MyClass.getView() — get item number " + position);
LogCat 은 아래처럼 출력될 것입니다.
I/MyActivity( 1557): MyClass.getView() — get item number 1
LogCat 사용하기
DDMS 에서 또는 ADB 셀에서 LogCat 을 사용할 수 있습니다. DDMS 내에서 LogCat 을 사용하는 방법에 대해서는 Using DDMS 를 참고하세요. ADB 셀에서 LogCat 을 실행하기 위한 일반적인 사용법은 다음과 같습니다.
[adb] logcat [<option>] ... [<filter-spec>] ...
개발용 컴퓨터에서 또는 에뮬레이터/장치 인스턴스의 원격 adb 셀에서 logcat 명령을 사용할 수 있습니다. 개발용 컴퓨터에서 로그 출력을 보기 위해서는 다음과 같이 합니다.
$ adb logcat
그리고 원격 adb 셀에서는 다음과 같이 합니다.
# logcat
아래 표는 logcat 명령어 라인 옵션을 나타냅니다.
-c |
Clears (flushes) the entire log and exits. |
-d |
Dumps the log to the screen and exits. |
-f <filename> |
Writes log message output to <filename> . The default is stdout . |
-g |
Prints the size of the specified log buffer and exits. |
-n <count> |
Sets the maximum number of rotated logs to <count> . The default value is 4. Requires the -r option. |
-r <kbytes> |
Rotates the log file every <kbytes> of output. The default value is 16. Requires the -f option. |
-s |
Sets the default filter spec to silent. |
-v <format> |
Sets the output format for log messages. The default is brief format. For a list of supported formats, see Controlling Log Output Format. |
로그 출력 필터링
모든 안드로이드 로그 메시지는 태그와 우선순위를 가지고 있습니다.
- 태그는 메시지가 어디에서 기원하는 지를 나타내는 짧은 문자열입니다. (예를 들면, "View" 는 view 시스템)
- 우선순위는 아래 문자 값들 중의 하나이며, 우선순위가 낮은 것에서부터 높은것 으로 정렬하였습니다.
V
— Verbose (lowest priority)D
— DebugI
— InfoW
— WarningE
— ErrorF
— FatalS
— Silent (highest priority, on which nothing is ever printed)
아래는 우선순위 레벨이 "I" 이고 태그가 "ActivityManager" 인 메시지를 출력하는 logcat 출력의 예입니다.
I/ActivityManager( 585): Starting activity: Intent { action=android.intent.action...}
로그 출력을 좀더 다루기 쉽도록 하기 위해서, 필터 표현식을 사용하여 로그 출력을 제한할 수 있습니다. 필터 표현식은 시스템에게 당신이 관심있는 태그-우선순위 조합만을 출력하도록 지정할 수 있습니다.
필터 표현식은 tag:priority ...와 같은 포맷을 따르며, tag 는 관심있는 태그를 가리키며, priority 는 그 태그에 대한 최소 레벨의 우선순위를 가리킵니다. 지정된 우선순위에 해당되는 또는 그 이상의 메시지만이 로그에 출력됩니다. 하나의 필터 표현식 내에 임의 개수의 tag:priority 명세를 제공할 수 있습니다. 일련의 명세는 공백으로 구분됩니다.
ActivityManager 태그와 "info" 우선순위, MyApp 태그와 "Debug" 우선순위를 지정하는 예는 다음과 같습니다.
adb logcat ActivityManager:I MyApp:D *:S위 표현식의 마지막 엘리먼트인 *:S 는 모든 태그에 대한 우선 순위를 "silent" 로 설정하여, "View" 와 "MyApp" 을 가지는 로그 메시지만이 표시되도록 합니다. *:S 를 사용하는 것은 명시적으로 나열한 필터만 출력하도록 하는 훌륭한 방법입니다.
아래 필터 표현식은 "warning" 우선순위 또는 그 이상의 모든 메시지를 출력합니다.
만약 개발용 컴퓨터(이와 다른 방법은 원격 adb 셀에서 실행되는 것) 에서 LogCat 을 실행하고 있다면, 환경 변수 ANDROID_LOG_TAGS 에 값을 설정함으로써 디폴터 필터를 설정할 수 있습니다.
export ANDROID_LOG_TAGS="ActivityManager:I MyApp:D *:S"
만일 원격 셀 또는 adb shell logcat 을 사용하여 LogCat을 실행중이라면, 에뮬레이터/디바이스 인스턴스에는 ANDROID_LOG_TAGS 필터가 적용되지 않는다는 것을 기억하세요.
로그 출력 포맷 제어하기
로그 메시지는 태그와 우선순위 외에도 많은 메타데이터 필드를 포함하고 있습니다. 출력 포맷을 수정함으로써 특정 메타데이터 필드를 표시하도록 할 수 있습니다. 그렇게 하기 위해서는, -v 옵션을 사용하며 아래에 나열된 출력 양식중 하나를 지정하면 됩니다.
brief
— Display priority/tag and PID of originating process (the default format).process
— Display PID only.tag
— Display the priority/tag only.thread
— Display process:thread and priority/tag only.raw
— Display the raw log message, with no other metadata fields.time
— Display the date, invocation time, priority/tag, and PID of the originating process.long
— Display all metadata fields and separate messages with blank lines.
[adb] logcat [-v <format>]
아래는 thread 출력 포맷을 표시하기 위한 방법을 보여줍니다.
adb logcat -v thread-v 옵션은 오직 하나만 지정할 수 있다는 것을 기억하세요.
선택적인 로그 버퍼 보기 (Viewing Alternative Log Buffers)
안드로이드 로깅 시스템은 로그 메시지를 위해 다수의 원형 버퍼(multiple circular bufers)를 유지합니다, 그리고 모든 로그 메시지가 디폴트 원형 버퍼에 전달되는 것은 아닙니다. 추가적인 로그 메시지를 보기 위해서는, 다른 원형 버퍼 보기를 요청하기 위해, -b 옵션을 이용할 수 있습니다. 아래는 사용가능한 버퍼 목록입니다.
radio
— View the buffer that contains radio/telephony related messages.events
— View the buffer containing events-related messages.main
— View the main log buffer (default)
[adb] logcat [-b <buffer>]radio 와 telephony 메시지를 포함하는 로그 버퍼를 보기위한 예제는 다음과 같습니다.
adb logcat -b radiostdout 과 stderr 보기
디폴트로, 안드로이 시스템은 stdout (표준 입력) 과 stderr (표준 에러) 를 /dev/null 로 보냅니다. Dalvik VM 을 실행하는 프로세스에 대해서는 시스템이 출력의 사본을 로그 파일에다가 작성하도록 할 수 있습니다. 이 경우, 시스템은 stdout 과 stderr 태그, 그리고 우선순위 I 를 가지는 메시지를 출력합니다.
이런식으로 출력을 재지정하기 위해서는, 실행중인 에뮬레이터/장치 인스턴스를 멈춥니다 그리고 출력 재지정을 활성화하기 위해
setprop
셀 명령을 사용합니다. $ adb shell stop시스템은 에뮬레이터/장치 인스턴스가 종료될 때까지 이 설정을 유지합니다. 에뮬레이터/장치 인스턴스에서 이 설정을 디폴트로 사용하기 위해서는,
$ adb shell setprop log.redirect-stdio true
$ adb shell start
/data/local.prop
에다가 값을 추가해 주면 됩니다.Web 애플리케이션 디버깅
안드로이드를 위한 웹 애플리케이션을 개발하고 있다면, 콘솔 JavaScript API 를 사용하여 JavaScript 를 디버그할 수 있습니다, 그리고 이것은 메시지를 LogCat 에 출력합니다. 상세한 정보는 Debugging Web Apps 를 보시기 바랍니다.
원본: http://developer.android.com/guide/developing/debugging/debugging-log.html