안드로이드 소스를 비교하는 방법에 대해 설명드리도록 하겠습니다.

 

 

우선 프로젝트를 먼저 실행해 줍니다.

 

저는 예시로 OldProject 와 NewProject를 소스를 비교할겁니다.

 

1. OldProject를 안드로이드 스튜디오로 실행 시켜 줍니다.

2. OldProject에서 아래와 그림과 같이 비교할 특정폴더(OldProject/app/main)를 선택합니다.

 

3. 마우스 오른쪽 클릭하여 메뉴를 엽니다.

4. 메뉴 중 Compare With를 선택합니다.

 

5. 비교할 프로젝트의 소스 폴더를 선택후 Open 버튼을 누릅니다.

예시)

OldProject/app/main

NewProject/app/main 선택

 

6. 짜잔~! OldProject/app/main과 NewProject/app/main의 소스파일들이 비교됩니다.

이런식으로

안드로이드 스튜디오에서 비교하면 어떤 소스가 추가 되었는지 또 어떤 소스가 없어졌는지 명확히 알수 있습니다.

 

728x90
반응형

태블릿을 개발하다보면 새로나오는 태블릿마다 해상도가 깨지는 현상이 발생한다.

DP로 정해줘도 태블릿이 새로나오면 UI가 깨지는 현상이 발생된다.

이유는

1. 해상도가 다르다.

2. DPI가 다르기 때문이다.


아래에 표를 보면 태블릿의 DP해상도는 크게 3개의 크기로 구분 할 수 있을거 같다.

1280 * 800

1024 * 768

960 * 600


*참고1) PX/DPI = DP , DP*DPI = PX

*참고2) smallestWidth(DP)는 가로,세로의 상관없이 가장 적은  비율의 값

*참고 표1 Orientation = LandScape 기준으로 작성

 기종

PX

DPI

  DP

smallestWidth(DP)  

WIDTH 

HEIGHT 

WIDTH 

 HEIGHT

Galaxy Note 10.1 2014 Edition

 2560

1600 

1280 

800 

 800

 Galaxy Note 10.1

 1280

800 

1280 

800 

800 

 Galaxy Tab 10.1

 1280

800 

1280 

800 

800 

 Galaxy Tab  8.9

1280 

800 

1280 

800 

800 

 Galaxy Tab A(2016)

1920 

1200 

1.5 

1280 

800 

800 

 Galaxy Tab S2

2048 

1536 

1024 

768 

768 

 Galaxy Tab A 9.7

1024 

768 

1024 

768 

768 

 Nexus 9 

2048 

1536 

1024 

768 

768 

 Galaxy Tab S3 

 2048

1536 

2.25 

910.2 

682.7 

682.7 

 Galaxy Tab E(W/B)

 1280

800 

1.33

961.5 

600.9 

600.9 

 G Pad3 8.0

 1920

1200 

960 

600 

600 


위에 표를 보았을 때 느낀점은 Galaxy Tab S3는 왜 DPI가 저럴까?;; 필자도 궁금하다.


태블릿마다 해상도가 깨지는 현상이 발생시 수정방법은 여러가지가 있겠지만 그중에 2가지를 들 수 있을거 같다.


방법 1.직접적으로 layout 폴더를 분기 처리한다.

장점

-부분적으로 고치기 딱좋다고 생각된다. UI가 중첩되거나 깨지는 부분만 고치면 되기 때문이다.

단점

-UI가 중첩되거나 깨지는 부분만 고치게 될 경우 UI가 언밸런스 해보일 수 있다.

 

참고 표2

 레이아웃 폴더 분기 종류

 설명

비고 

 layout

 기본레이아웃

 

layout-port

 가로모드시 레이아웃 적용

 

layout-land 

세로모드시 레이아웃 적용 

 

layout-sw600dp 

smallestWidth의 값이 600dp이상일 경우 적용

 1. smallestWidth의 값이 650dp이고 layout-sw600dp, layout-sw651dp폴더가 있을경우 smallestWidth 650dp의 값은 layout-sw600dp에 영향을 받는다.

2. 폴더명 뒤에 -port,-land는 적용 되지 않는다.

 layout-w600dp-port

세로모드의 넓이 dp값이 600이상일 경우 레이아웃 적용

세로모드의 넓이 dp값이 650이고layout-w600dp-port, layout-w651dp-port폴더가 있을경우 650dp의 값은 layout-w600dp-port에 영향을 받는다.

 layout-w600dp-land

가로모드의 넓이 dp값이 600이상일 경우 레이아웃 적용

가로모드의 넓이 dp값이 650이고 layout-w600dp-port, layout-w651dp-port폴더가  있을경우 650dp의 값은 layout-w600dp-port에 영향을 받는다.


방법 2.직접적으로 value폴더의 diman.xml를 분기 처리한다.

장점

-UI를 한꺼번에 고치기 딱 좋다. 깨지는 태블릿의 비율만 계산하여 폴더 분기 처리하면 한꺼번에 적용되기 때문이다.

단점

-비율계산이 잘못될 경우 기존 UI가 망가질수 있다.

참고 표3

-해상도 비율

 해상도

 비율

1280*800 

8:5 

1024*768 

4:3 

960*600 

8:5 









728x90
반응형

안드로이드 앱설치 여부를 확인하기 위해서는 해당앱의 패키지 명을 알고 있어야합니다.


isInstallPackage_2(this,"com.test.pkgname");

/**
* 설치여부를 판단한다
* @param context context
* @param pkgName 패키지명
* @return 설치여부
*/
public static String isInstallPackage_2(Context context, String pkgName){
PackageManager manager = context.getPackageManager();
PackageInfo pi;
try {
pi = manager.getPackageInfo(pkgName, PackageManager.GET_META_DATA);
if(pi!=null){
return pi.versionName+"";
}
} catch (NameNotFoundException e) {
}
return null;
}


728x90
반응형

매니페스트에 다음과 같이 밑줄 된 값이 필요할때 다음과 같이 가져온다.


AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.si.pe.shin.library"
android:versionCode="1"
android:versionName="3.7.0" >

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:description="@string/app_name"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".activity.MainActivity"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

/**
* 매니페스트 버전을 얻는다
* @param context context
* @return 버전
*/
public static String getVersionName(Context context) {
String version = "";
try {
PackageInfo i = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
version = i.versionName + "";
} catch(NameNotFoundException e) {
}
return version;
}
/**
* 매니페스트 버전코드를 얻는다
* @param context context
* @return 버전코드
*/
public static int getVersionCode(Context context) {
int versionCode = 1;
try {
PackageInfo i = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
versionCode = i.versionCode;
} catch(NameNotFoundException e) {
}
return versionCode;
}
/**
* 매니페스트 Description를 얻는다
* @param context context
* @return 어플아이디
*/
public static String getDescription(Context context) {
String appId = "";
try {
PackageManager pm = context.getPackageManager();
PackageInfo i = pm.getPackageInfo(context.getPackageName(), 0);
appId = i.applicationInfo.loadDescription(pm) + "";
} catch(NameNotFoundException e) {
}
return appId;
}

/**
* 매니페스트 Label을 얻는다
* @param context context
*/
public static String getAppLabel(Context context) {
String appName = "";
try {
PackageManager pm = context.getPackageManager();
PackageInfo i = pm.getPackageInfo(context.getPackageName(), 0);
appName = i.applicationInfo.loadLabel(pm) + "";
} catch(NameNotFoundException e) {
}
return appName;
}


728x90
반응형

개발을 하다보면 특정앱은 백그라운드에 남기지 말아야할때가 있습니다.


그럴때는 


        <activity

            android:name=".MainActivity"

            android:label="@string/app_name" 

            android:excludeFromRecents="true"

            >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>


밑에 코드를 매니페스트 액티비티에 사용해주시면 앱이 백그라운드에 남아 있지않게 됩니다.

android:excludeFromRecents="true"


밑에 그림을 보시면 현재 저는 Test을 실행시키고 스마트폰 백그라운드를 열었습니다.

이렇게 그림만 보면 모르겠지만 원래는 Test앱이 네이버앱 앞에 표시되어야합니다.

하지만 저는 android:excludeFromRecents="true"해당 코드를 사용하여

앱이 백그라운드에 표시되지 않도록 하였습니다.

728x90
반응형

/**파일 확장자 가져오기
* @param fileStr 경로나 파일이름
* @return*/
public static String getExtension(String fileStr){
String fileExtension = fileStr.substring(fileStr.lastIndexOf(".")+1,fileStr.length());
return TextUtils.isEmpty(fileExtension) ? null : fileExtension;
}

/**파일 이름 가져오기
* @param fileStr 파일 경로
* @param isExtension 확장자 포함 여부
* @return */
public static String getFileName(String fileStr , boolean isExtension){
String fileName = null;
if(isExtension)
{
fileName = fileStr.substring(fileStr.lastIndexOf("/"),fileStr.lastIndexOf("."));
}else{
fileName = fileStr.substring(fileStr.lastIndexOf("/")+1);
}
return fileName;
}


728x90
반응형


FileCache.java

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

public interface FileCache {

public FileEntry get(String key);

public void put(String key, ByteProvider provider) throws IOException;

public void put(String key, InputStream is) throws IOException;

public void put(String key, File sourceFile, boolean move) throws IOException;

public void remove(String key);

public void clear();

public boolean has(String key);
}


FileEntry.java

public class FileEntry {

private String key;
private File file;

public FileEntry(String key, File file) {
this.key = key;
this.file = file;
}

public InputStream getInputStream() throws IOException {
return new BufferedInputStream(new FileInputStream(file));
}

public String getKey() {
return key;
}

public File getFile() {
return file;
}

}


FileCacheImpl.java

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

public class FileCacheImpl implements FileCache {

private CacheStorage cacheStorage;

public FileCacheImpl(File cacheDir, int maxKBSizes) {
long maxBytesSize = maxKBSizes <= 0 ? 0 : maxKBSizes * 1024;
cacheStorage = new CacheStorage(cacheDir, maxBytesSize);
}

@Override
public FileEntry get(String key) {
File file = cacheStorage.get(keyToFilename(key));
if (file == null) {
return null;
}
if (file.exists()) {
return new FileEntry(key, file);
}
return null;
}

@Override
public void put(String key, ByteProvider provider) throws IOException {
cacheStorage.write(keyToFilename(key), provider);
}

@Override
public void put(String key, InputStream is) throws IOException {
put(key, ByteProviderUtil.create(is));
}

@Override
public void put(String key, File sourceFile, boolean move)
throws IOException {
if (move) {
cacheStorage.move(keyToFilename(key), sourceFile);
} else {
put(key, ByteProviderUtil.create(sourceFile));
}
}

@Override
public void remove(String key) {
cacheStorage.delete(keyToFilename(key));
}

private String keyToFilename(String key) {
String filename = key.replace(":", "_");
filename = filename.replace("/", "_s_");
filename = filename.replace("\\", "_bs_");
filename = filename.replace("&", "_bs_");
filename = filename.replace("*", "_start_");
filename = filename.replace("?", "_q_");
filename = filename.replace("|", "_or_");
filename = filename.replace(">", "_gt_");
filename = filename.replace("<", "_lt_");
return filename;
}

@Override
public void clear() {
cacheStorage.deleteAll();
}

@Override
public boolean has(String key) {
return cacheStorage.has(key);
}


}


IOUtils.java

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;

public abstract class IOUtils {

public static String read(InputStream is) throws IOException {
InputStreamReader reader = null;
try {
reader = new InputStreamReader(is);
StringBuilder builder = new StringBuilder();
char[] readDate = new char[1024];
int len = -1;
while ((len = reader.read(readDate)) != -1) {
builder.append(readDate, 0, len);
}
return builder.toString();
} finally {
close(reader);
}
}

public static void copy(InputStream is, OutputStream out)
throws IOException {
byte[] buff = new byte[4096];
int len = -1;
while ((len = is.read(buff)) != -1) {
out.write(buff, 0, len);
}
}


public static void copy(File source, OutputStream os) throws IOException {
BufferedInputStream is = null;
try {
is = new BufferedInputStream(new FileInputStream(source));
IOUtils.copy(is, os);
} finally {
IOUtils.close(is);
}
}

public static void copy(InputStream is, File target) throws IOException {
OutputStream os = null;
try {
os = new BufferedOutputStream(new FileOutputStream(target));
IOUtils.copy(is, os);
} finally {
IOUtils.close(os);
}
}

public static void copy(String str, OutputStream os) throws IOException {
os.write(str.getBytes());
}

public static void close(Closeable stream) {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
}

}


FileCacheFactory.java

import android.content.Context;
import java.io.File;
import java.util.HashMap;

public class FileCacheFactory {

private static boolean initialized = false;
private static FileCacheFactory instance = new FileCacheFactory();

public static void initialize(Context context, String file_dir) {
if (!initialized) {
synchronized (instance) {
if (!initialized) {
instance.init(context,file_dir);
initialized = true;
}
}
}
}

public static FileCacheFactory getInstance() {
if (!initialized) {
throw new IllegalStateException(
"Not initialized. You must call FileCacheFactory.initialize() before getInstance()");
}
return instance;
}

private HashMap<String, FileCache> mCacheMap = new HashMap<String, FileCache>();
private File mCacheBaseDir;

private FileCacheFactory() {
}

private void init(Context context) {
mCacheBaseDir = context.getCacheDir();
}

private void init(Context context, String file_dir) {
// cacheBaseDir = context.getCacheDir();
mCacheBaseDir = new File(file_dir);
}

public FileCache create(String cacheName, int maxKbSizes) {
synchronized (mCacheMap) {
FileCache cache = mCacheMap.get(cacheName);
File cacheDir = new File(mCacheBaseDir, cacheName);
if (cache != null) {
try {
cache = new FileCacheImpl(cacheDir, maxKbSizes);
mCacheMap.put(cacheName, cache);
} catch (Exception e) {
String.format("FileCache[%s] Aleady exists", cacheName);
}
}


return cache;
}
}

public FileCache get(String cacheName) {
synchronized (mCacheMap) {
FileCache cache = mCacheMap.get(cacheName);
if (cache == null) {
try {

}catch (Exception e)
{
String.format("FileCache[%s] not founds.", cacheName);
}
}
return cache;
}
}

public void destroy(String cacheName)
{
FileCache cache = mCacheMap.get(cacheName);

File file = new File(mCacheBaseDir+File.separator+cacheName);
if(file.exists())
{
file.delete();
}
}

public void clear(){
mCacheMap.clear();
}

public boolean has(String cacheName) {
return mCacheMap.containsKey(cacheName);
}
}

설명

캐시 디렉토리 안에 캐시파일이 여러개 저장되는 방식입니다. 


사용방법

1. 캐시 디렉토리

private FileCache mFileCache = null;

public static final String CACHE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "FolderName" + File.separator + ".cache"; //캐시 기본폴더

FileCacheFactory.initialize(mContext, CACHE_PATH);
if (!FileCacheFactory.getInstance().has(zipDirName)) // 해당 키의 캐시 디렉토리가 있는지 확인
{
FileCacheFactory.getInstance().create(zipDirName, 0); // 캐시디렉토리가 없을경우 만든다.
}
mFileCache = FileCacheFactory.getInstance().get(zipDirName); // 해당 파일의 캐시 디렉토리를 가져온다.


- 캐시 객체 생성

FileCacheFactory.initialize(mContext, CACHE_PATH);

- 캐시디렉토리 존재 여부 체크

FileCacheFactory.getInstance().has(Dirkey)

- 캐시디렉토리 생성

FileCacheFactory.getInstance().create(Dirkey, 0); // 캐시디렉토리가 없을경우 만든다.

- 캐시디렉토리 가져오기

mFileCache = FileCacheFactory.getInstance().get(Dirkey); // 해당 파일의 캐시 디렉토리를 가져온다.


2. 캐시 

-캐시 저장

/**
* @param key cache
* @param val cache 내용
* @param isMove ture = val파일이 캐시경로로 이동됨, false = val파일이 캐시경로로 복사됨
*/
private void setCacheFile(String key, File val, boolean isMove) {
if (!mFileCache.has(key)) {
try {
mFileCache.put(key, val, isMove);
} catch (IOException e) {
e.printStackTrace();
}
}
}

-캐시 가져오기

/**
* 캐시내용을 가져온다.
* @param key cache
* @return
*/
public FileEntry getCacheFile(String key) {

return mFileCache.get(key);
}


chche.zip


728x90
반응형
/**
* 용량계산
* @param size
* @return
*/
public static String sizeCalculation(long size) {
String CalcuSize = null;
int i = 0;

double calcu = (double) size;
while (calcu >= 1024 && i < 5) { // 단위 숫자로 나누고 한번 나눌 때마다 i 증가
calcu = calcu / 1024;
i++;
}
DecimalFormat df = new DecimalFormat("##0.0");
switch (i) {
case 0:
CalcuSize = df.format(calcu) + "Byte";
break;
case 1:
CalcuSize = df.format(calcu) + "KB";
break;
case 2:
CalcuSize = df.format(calcu) + "MB";
break;
case 3:
CalcuSize = df.format(calcu) + "GB";
break;
case 4:
CalcuSize = df.format(calcu) + "TB";
break;
default:
CalcuSize="ZZ"; //용량표시 불가

}
return CalcuSize;
}




728x90
반응형

'Java' 카테고리의 다른 글

자바 랜덤  (0) 2017.05.23
java 정렬  (0) 2017.05.23
액티비티 할당된 메모리 즉시 반환하기  (0) 2016.01.12
Java Null Check 코드  (0) 2015.06.29
Java 인스턴스 하나만 사용하기(싱글턴 패턴)  (0) 2015.06.29

+ Recent posts